Продолжение о том, как организовать структуру кода и пакетов в подходе DDD в Go

Пришло время взглянуть на лучшее архитектурное решение при использовании DDD в проектах Go. В этой статье мы возьмем репозиторий, в котором есть все компоненты DDD, и покажем, как можно управлять менее сложной настройкой проекта, сохраняя при этом DDD.

Репозиторий, который мы изменим, взят из моей предыдущей статьи о том, как реализовать DDD в Go. В этой статье я объяснил все компоненты, присутствующие в DDD, как объяснил Эрик Эванс. Репозиторий можно найти на GitHub.



Все изображения, нарисованные в этой статье, нарисованы Перси Больмером, суслик нарисован Такуя Уэда, вдохновленным работами Рене Френч. На изображениях изменен суслик.

Перемещение агрегатов в их доменный пакет

Первое, что нужно сделать, - это полностью удалить пакет aggregate. Мы узнали, что такое агрегатирование и какие правила следует применять, нам не обязательно давать имя пакету для агрегирования. Я обычно помещаю агрегаты в соответствующий пакет домена, поэтому агрегат Customer должен находиться внутри домена customer. На мой взгляд, в этом гораздо больше смысла.

Конечно, после перемещения файлов в домен customer вам также необходимо изменить любую ссылку, указывающую на aggregate.Customer, и заменить ее на customer.Customer или Customer, если вы находитесь в клиентском пакете.

То же самое нужно сделать для агрегата Product, он должен находиться внутри домена product. Я не буду описывать все изменения кода, все упоминания о пакете aggregate должно быть достаточно легко найти и провести рефакторинг.

По моему мнению, таким образом выглядит намного лучше, если агрегат в их доменном пакете, это также имеет больше смысла.

Удаляем совокупную папку, и мы убираем часть запаха кода.

Перемещение объектов-ценностей и сущностей

У нас все еще есть папки для entity и valueobject, что, я думаю, не так. Одна вещь, которая хороша в таком хранении общих структур в отдельном пакете, - это избегать циклического импорта.

Однако мы можем добиться этого другим способом, менее раздутым. Прямо сейчас у нас вообще нет корневого пакета. Переместите все файлы из entity и valueobject в корень и переименуйте пакет в tavern

Это оставляет нам еще более улучшенную структуру.

Вам также нужно будет переименовать ваш модуль в go.mod в соответствующее имя.

module github.com/percybolmer/tavern

Мало того, нам нужно изменить весь импорт во всех файлах, чтобы отразить это изменение, и все ссылки пакета entities должны быть изменены на tavern, как показано ниже.

Все операции импорта, начинающиеся с github.com/percybolmer/go-ddd, изменяются на github.com/percybolmer/tavern.

Самый простой способ применить это изменение - изменить все файлы, удалить файлы go.mod и go.sum и повторно инициализировать модуль go с новым именемgo mod init github.com/percybolmer/tavern.

Это было неплохое упражнение для рефакторинга, но оно того стоило. Не стесняйтесь повторно запускать любые тесты, чтобы знать, что он работает должным образом.

Разделение пакета услуг

Прямо сейчас пакет услуг содержит все услуги в одном пакете. Я хотел бы разделить их на два пакета: Order и Tavern.

Причина в том, что по мере роста проектов хорошо, когда услуги разделяются на более мелкие пакеты. На мой взгляд, хранение их всех в одном пакете может стать слишком большим. Мне также нравится сохранять домен как часть инфраструктуры, в этом случае мы создаем новую папку с именем order в папке services. Причина в том, что в будущем мы можем увидеть больше услуг, связанных с заказами, например, текущая служба заказов фокусируется на Customer, который заказывает напиток, но как насчет того, когда таверне нужно пополнить запасы? Используя эту структуру, разработчики могут легко узнать, где искать связанный код.

Еще одна важная вещь - это именование функций конфигурации. Если мы продолжаем создавать такие функции, как WithMemoryCustomerRepository, трудно поддерживать, какая конфигурация и куда идет. Намного легче, если мы видим order.WithMemoryCustomerRepository, чтобы знать, что происходит.

Внесение этого изменения требует, чтобы пакет Tavern ссылался на order.OrderService вместо единственного OrderService

Измените все ссылки, чтобы они соответствовали новой структуре. Мы также добавим новую функцию в OrderService для добавления новых клиентов, потому что сейчас мы можем сделать это только с помощью репозитория customer внутри структуры. Это не сработает, поскольку сервис будет доступен для других пакетов. Кроме того, служба никогда не должна предполагать, что пользователь службы знает, как действовать таким образом, поэтому вполне естественно, что эта логика поддерживается в собственном домене службы.

После применения этих изменений выполнить тест для заказа продукта стало еще проще.

Наконец-то у нас есть окончательное решение. Очень простой в навигации, четко структурированный проект.

Как запустить таверну

И последнее, что я хотел бы порекомендовать, - это использовать папку cmd, которая есть во многих репозиториях Go. В этой папке должны быть найдены все инструменты командной строки, а их может быть несколько. В заключение этой статьи я создам основную программу в папке cmd.

mkdir cmd
touch main.go

Давай закажем пива, чтобы отпраздновать.

На этом завершается эта очень длинная серия из двух частей о DDD.

Вы можете найти полный код на GitHub.

Еще многое предстоит узнать о DDD, это касается только основ и того, как я нахожу структурированные проекты. Мы еще не затронули темы Domain events, CQRS и EventSourcing. Следите за новостями.

Если вы хотите узнать больше о DDD, я рекомендую книгу Эрика Эванса Domain-Driven Design: Tackling Complexity in the Heart of Software. Вы можете найти ее на Amazon.

Обратите внимание, что ссылка является партнерской, что означает, что мне также платят, когда она используется для покупки продукта.

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

Я доступен здесь на Medium, Twitter, Instagram и Linkedin.