СоХабр закрыт.

С 13.05.2019 изменения постов больше не отслеживаются, и новые посты не сохраняются.

H Змейка на С# и Onion aka Clean Architecture в черновиках Tutorial


В прошлой своей статье я вскользь упомянул Onion архитектуру. Там было в основном про то что не надо лишние библиотеки и папки создавать, что надо делить код на модули и что можно писать код прямо в контроллер и использовать Entity Framework там же напрямую НО Домен все равно должен быть отдельно и все вычисления должны быть в нем. Теперь хочу поговорить о луковой архитектуре. Больше всего подходящей для сложного приложения с большим количеством логики. В этой статье в качестве примера я буду использовать игру "Змейка" потому что в ней достаточно логики для «богатого» домена



Легенда


  1. Красным цветом указана зависимости.
  2. Зеленым цветом указаны важные группы. Под — слои
  3. Синим цветом разделены основные слоим которые желательно держать отдельно


Терминология


  1. Анемичная модель: Модель данных без логики.
  2. ValueObject: Неизменяемое хранилище данных без уникального идентификатор
  3. Entity: Объект c уникальным идентификатором
  4. Aggregate: Объект содержащий в себе Entity. Обладает уникальным идентификатором.
  5. Data Layer — Repository: Изолирует нас от того что мы используем. Хранилище наших Aggregate
  6. UseCase/Interactor: Изолирует нас от того что использует нас. Например от ASP.NET, gRPC, WCF, WPF, WinForms. Может вызывать Repository когда это нужно. Сам почти ничего не делает и не хранит. Тут должна быть минимальная логика. Только использует Entity, Aggregate, ValueObject и Repository. В Анемичной модели наоборот делает все, тут вся логика.
  7. DomainEvent: Событие которое случилось в Домене и на которое внешний мир должен как-то среагировать. Можно сделать просто булево свойство или коллекцию с объектами у вашего домена в которую добавлять события. Обрабатывать их можно в ApplicationService/UseCase/Interactor.


Суть


Union aka Clean это про то что у вас должен быть отдельно:

  1. Класс, который делает вычисления или является объектным представление какой-то сущности в приложении. Calculator и ResultValueObject
  2. Класс, который пишет данные в хранилище данных или читает их оттуда. CalculatorRepository
  3. Класс, который реагируют на действия пользователя. CalculatorService
  4. Calculator и ResultValueObject должны быть независимы от CalculatorRepository и CalculatorService


Рекомендуемая структура проекта


Для каждого модуля своя папка. Модуль может состоять из мелких под модулей. Тогда для них создаются папки внутри папки основного модуля. Рассматривайте каждый модуль как отдельное мини приложение которое решает какую-то свою определенную задачу.


Пример: Игра змейка


Для простоты просмотра пометил весь код в один файл.
github.com/VictoremWinbringer/SnakeGameWithOnionAkaCleanArchitecture

Литература


  1. Domain Driven Design – simplifying the complicated
  2. Заблуждения Clean Architecture


UPD


В комментариях пожаловались что картинка у меня кривая поэтому добавил еще одну.
Тут замечание: Gateway == Repository

комментарии (6)

+2
yarosroman ,  
Как написать операционную систему. github.com/torvalds/linux
+1
TimurNes ,  
Теперь хочу поговорить о луковой архитектуре.
15 коротких предложений «на поговорить» про луковую архитектуру — это, конечно, сильно.

DomainServices: Изолирует нас от того что мы используем. Например от ADO.NET, NLogger, Redis, RabbitMQ, AppMetrics, ElasticSearch
Доменные сервисы содержат доменную логику, а не изолирует от того, что мы используем. Домен максимум знает про интерфес IUserRepository. Сам репозиторий, как имплементированный сервис — это инфраструктурный слой, но уж никак не доменный.
0
+1 –1
VanquisherWinbringer ,   * (был изменён)
del
0
Wyrd ,  

Корявость картинки намекает на корявость архитектуры? Или нет?

+4
Rikkitik ,  
Корявость картинки и самого текста, видимо, должна проиллюстрировать степень серьёзности подхода автора к проблеме. И, надо сказать, иллюстрирует отлично.
+1
Ascar ,   * (был изменён)
Вот предположим то что вы написали про слоеность архитектуры имеет хоть какой то смысл. Смотрим проект и видим что у вас абсолютно все лежит в Program.cs. И первое апреля уже прошло.