В коде:
<?php namespace App;
class SendReceipt {}
Структура папок:
src
Receipt
ReceiptRepository
SendReceipt
SendReceiptHandler
User
UserRepository
CreateUser
CreateUserHandler
Преимущества:
- Проще не иметь дело с подпространствами имён. В очень маленьких приложениях это может быть нормально. Если у вас есть 5 классов, кто сказал, что вам вообще нужны подпространства? Если это одиночный пакет с определённым назначением или приложение, у которого есть только 1 «модуль», возможно, не нужно ничего больше, чем одно глобальное пространство имён.
Недостатки:
- В тот момент, когда в ваше приложение прийдёт определённая степень сложности, будет сложно находить классы в большой массе глобального пространства имён. Если у вас есть разделение сущностей и назначений в ваших классах, например, Пользователи и Рецепты — глобальное пространство имён скидывает всё в одну большую кучу. Совсем не модульно.
2. Группировка по паттернам
В коде:
<?php namespace App\Commands;
class SendReceipt {}
Структура папок:
src
Commands
SendReceipt
CreateUser
Entities
Receipt
User
Handlers
SendReceiptHandler
CreateUserHandler
Repositories
ReceiptRepository
UserRepository
Преимущества:
- Когда вам нужно найти команду, вы точно знаете, где её искать. Если ваш мозг говорит: «Мне нужно отредактировать одну из моих команд. Какую именно? Ту которая шлёт рецепты», — то этот способ вполне подходит. Эта одноуровневая организация лучше, чем игнорирование пространств имён, но и не глубокая, таким образом она вполне может подойти для средних сайтов.
- Кроме того, ваши связанные классы (например, команды) могут жить рядом друг с другом. Вы сможете найти некоторые сходства, например, между командами
SendReceipt
и SendReminder
.
- Этот метод также позволяет вам проектировать связи между классами программно. Например,
Command Bus
может всегда знать, что обработчик команды (которая живёт вApp\Commands\{commandName}
) всегда находится в App\Handlers\{commandName}Handler
.
Недостатки:
- Этот способ организации разносит ваши классы из одного контекста по различным пространствам имён. Например, у вас может быть
App\Commands\SendReceipt
,App\Receipt
или App\Entities\Receipt
, App\Providers\ReceiptServiceProvider
,App\Handlers\Commands\SendReceiptHandler
, App\Repositories\ReceiptRepository
и т.д. Вся логика рецептов разнесена по различным местам.
- Если вы сфокусированы на модульности и инкапсулированности — этот способ точно не победитель. Потому как вы разносите весь ваш код, например, оплаты, по всему пространству имён. Этот способ организации не фокусирует классы оплаты в одном модуле. Классы находятся рядом только потому, что они связаны архитектурным паттерном, а не потому, что они действительно связаны по смыслу.
3. Группировка по контексту
В коде:
<?php namespace App\Billing;
class SendReceipt {}
Структура папок:
src
Billing
Receipt
ReceiptRepository
SendReceipt
SendReceiptHandler
User
User
UserRepository
CreateUser
CreateUserHandler
Преимущества:
- Если вы работаете исключительно над Billing в данный момент, вы знаете, что у вас есть всё, связанное с этим, в одном месте. Для ваших рецептов — сущность, команды, обработчики, репозиторий и т.д. — всё в одном месте, красиво связано и находится по одному адресу в одной группе.
- Именно здесь мы начинаем экспериментировать с инкапсуляцией и модульностью. Все наши Billing классы, независимо от их паттерна, находятся в одном месте, что помогает сгруппировать их ментально. Мы можем думать о них как об отдельном модуле, который живёт в нашем приложении.
Недостатки:
- Ваши команды теперь разбросаны по всему коду. Ваши репозитории — тоже. И сущности. И ваши обработчики.
4. Группировка по контексту и паттернам
В коде:
<?php namespace App\Billing\Commands;
class SendReceipt {}
Структура папок:
src
Billing
Entities
Receipt
Repositories
ReceiptRepository
Commands
SendReceipt
Handlers
SendReceiptHandler
User
Entities
User
Repositories
UserRepository
Commands
CreateUser
Handlers
CreateUserHandler
Преимущества:
- Разделение таким способом даёт вам большой уровень разделения пространств имён. Это очень полезно, если у вас большая база кода с большим количеством классов — чем больше классов, тем больше вы будете ценить такой способ разделения.
- Как и в группировке по паттерну, вы можете программно связывать ваши классы.
- И пока ваши классы сгруппированы по паттерну, они всё ещё сгруппированы по контексту. У вас есть модульность и группировка по контексту одновременно.
Недостатки:
- Чем больше ваше пространство имён, тем больше умственной энергии вы потратите на понимание всего пространства имён. Для маленьких и средних приложений это может быть разрушительно.
- Из-за группировки по паттернам на низком уровне у вас больше нет такой же простой организации, как при организации по контексту.
Пример
Небольшой пример организации кода по каждому способу:
Одно глобальное пространство имён
app
Conference
ConferenceRepository
CreateConference
CreateConferenceHandler
CreateTalk
CreateTalkHandler
DeleteConference
DeleteConferenceHandler
DeleteTalk
DeleteTalkHandler
ProposeTalkToConference
ProposeTalkToConferenceHandler
RetractTalkProposal
RetractTalkProposalHandler
Talk
TalkRepository
UpdateConference
UpdateConferenceHandler
UpdateTalk
UpdateTalkHandler
Группировка по паттерну
app
Commands
CreateConference
CreateTalk
DeleteConference
DeleteProposal
DeleteTalk
ProposeTalkToConference
RetractTalkProposal
UpdateConference
UpdateTalk
Entities
Conference
Proposal
Talk
Handlers
CreateConferenceHandler
CreateTalkHandler
CreateProposalHandler
DeleteConferenceHandler
DeleteProposalHandler
DeleteTalkHandler
ProposeTalkToConferenceHandler
RetractTalkProposalHandler
UpdateConferenceHandler
UpdateTalkHandler
Repositories
ConferenceRepository
TalkRepository
Группировка по контексту
app
Conferences
Conference
ConferenceRepository
CreateConference
CreateConferenceHandler
DeleteConference
DeleteConferenceHandler
UpdateConference
UpdateConferenceHandler
Talks
CreateTalk
CreateTalkHandler
DeleteTalk
DeleteTalkHandler
ProposeTalkToConference
ProposeTalkToConferenceHandler
Talk
TalkRepository
RetractTalkProposal
RetractTalkProposalHandler
UpdateTalk
UpdateTalkHandler
Группировка по контексту и паттерну
app
Conferences
Commands
CreateConference
DeleteConference
UpdateConference
Entities
Conference
Handlers
CreateConferenceHandler
DeleteConferenceHandler
UpdateConferenceHandler
Repositories
ConferenceRepository
Talks
Commands
CreateTalk
DeleteTalk
ProposeTalkToConference
RetractTalkProposal
UpdateTalk
Entities
Talk
Handlers
CreateTalkHandler
DeleteTalkHandler
ProposeTalkToConferenceHandler
RetractTalkProposalHandler
UpdateTalkHandler
Repositories
TalkRepository
Вывод
Так какой же ответ?
Он различен в зависимости от каждой конкретной ситуации.
Возможно, что более простая организация лучше работает с маленьким количеством классов и сущностей, а более сложная структура организации подходит для более сложных систем. Но это не жёсткое правило. Я даже не уверен, что это вообще правило.
Я думаю, модульность и инкапсуляция даёт вашему разуму больше пищи для размышления. Даёт вам возможность думать о проектировании каждого подпространства модулем, который может быть в любой момент удалён.
В любом случае, всё зависит только от вас.