← Заметки и статьи о Laravel PHP

19 апреля 2015 перевод

Способы организации пространств имён классов

1. Одно глобальное пространство имён

В коде:

<?php namespace App;

class SendReceipt {}

Структура папок:

src
    Receipt
    ReceiptRepository
    SendReceipt
    SendReceiptHandler
    User
    UserRepository
    CreateUser
    CreateUserHandler

Преимущества:

  • Проще не иметь дело с подпространствами имён. В очень маленьких приложениях это может быть нормально. Если у вас есть 5 классов, кто сказал, что вам вообще нужны подпространства? Если это одиночный пакет с определённым назначением или приложение, у которого есть только 1 «модуль», возможно, не нужно ничего больше, чем одно глобальное пространство имён.

Недостатки:

  • В тот момент, когда в ваше приложение прийдёт определённая степень сложности, будет сложно находить классы в большой массе глобального пространства имён. Если у вас есть разделение сущностей и назначений в ваших классах, например, Пользователи и Рецепты — глобальное пространство имён скидывает всё в одну большую кучу. Совсем не модульно.

2. Группировка по паттернам

В коде:

<?php namespace AppCommands;
    
class SendReceipt {}

Структура папок:

src
    Commands
        SendReceipt
        CreateUser
    Entities
        Receipt
        User
    Handlers
        SendReceiptHandler
        CreateUserHandler
    Repositories
        ReceiptRepository
        UserRepository

Преимущества:

  • Когда вам нужно найти команду, вы точно знаете, где её искать. Если ваш мозг говорит: «Мне нужно отредактировать одну из моих команд. Какую именно? Ту которая шлёт рецепты», — то этот способ вполне подходит. Эта одноуровневая организация лучше, чем игнорирование пространств имён, но и не глубокая, таким образом она вполне может подойти для средних сайтов.
  • Кроме того, ваши связанные классы (например, команды) могут жить рядом друг с другом. Вы сможете найти некоторые сходства, например, между командами SendReceipt и SendReminder.
  • Этот метод также позволяет вам проектировать связи между классами программно. Например, Command Bus может всегда знать, что обработчик команды (которая живёт в AppCommands{commandName}) всегда находится в AppHandlers{commandName}Handler.

Недостатки:

  • Этот способ организации разносит ваши классы из одного контекста по различным пространствам имён. Например, у вас может быть AppCommandsSendReceipt, AppReceipt или AppEntitiesReceipt, AppProvidersReceiptServiceProvider, AppHandlersCommandsSendReceiptHandler, AppRepositoriesReceiptRepository и т.д. Вся логика рецептов разнесена по различным местам.
  • Если вы сфокусированы на модульности и инкапсулированности — этот способ точно не победитель. Потому как вы разносите весь ваш код, например, оплаты, по всему пространству имён. Этот способ организации не фокусирует классы оплаты в одном модуле. Классы находятся рядом только потому, что они связаны архитектурным паттерном, а не потому, что они действительно связаны по смыслу.

3. Группировка по контексту

В коде:

<?php namespace AppBilling;
    
class SendReceipt {}

Структура папок:

src
    Billing
        Receipt
        ReceiptRepository
        SendReceipt
        SendReceiptHandler
    User
        User
        UserRepository
        CreateUser
        CreateUserHandler

Преимущества:

  • Если вы работаете исключительно над Billing в данный момент, вы знаете, что у вас есть всё, связанное с этим, в одном месте. Для ваших рецептов — сущность, команды, обработчики, репозиторий и т.д. — всё в одном месте, красиво связано и находится по одному адресу в одной группе.
  • Именно здесь мы начинаем экспериментировать с инкапсуляцией и модульностью. Все наши Billing классы, независимо от их паттерна, находятся в одном месте, что помогает сгруппировать их ментально. Мы можем думать о них как об отдельном модуле, который живёт в нашем приложении.

Недостатки:

  • Ваши команды теперь разбросаны по всему коду. Ваши репозитории — тоже. И сущности. И ваши обработчики.

4. Группировка по контексту и паттернам

В коде:

<?php namespace AppBillingCommands;

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

Вывод

Так какой же ответ?

Он различен в зависимости от каждой конкретной ситуации.

Возможно, что более простая организация лучше работает с маленьким количеством классов и сущностей, а более сложная структура организации подходит для более сложных систем. Но это не жёсткое правило. Я даже не уверен, что это вообще правило.

Я думаю, модульность и инкапсуляция даёт вашему разуму больше пищи для размышления. Даёт вам возможность думать о проектировании каждого подпространства модулем, который может быть в любой момент удалён.

В любом случае, всё зависит только от вас.

Источник: https://mattstauffer.co/blog/how-to-organize-class-namespaces#namespacing-approaches

Плюсануть
Поделиться
Отправить