Re[13]: DDD протаскивание других слоев через параметры методов Domain
От: samius Япония http://sams-tricks.blogspot.com
Дата: 27.11.20 23:20
Оценка: +1
Здравствуйте, #John, Вы писали:

J>Здравствуйте, samius, Вы писали:


S>>Бизнес логики я в этом примере не увидел. Хотелось посмотреть на чуть более сложном примере, где взаимодействуют покупатель, корзина товаров, набор акций на отдельные группы товаров, накопительные бонусные программы или что-то в этом роде.

S>>Я не прошу приводить код, я его довольно ясно представляю, в том числе с тестами и обилием моков.

J>>>Если придется работать с внешним сервисом, придется создавать DomainService и дробить бизнес логику в rich-моделях.

S>>Проблема рич в том, что чем больше сценариев, тем больше придется дробить бизнес логику в рич моделях.
S>>Та же валидация внутри рич моделей говорит "ой", когда внезапно выясняется, что для одних процессов должна быть одна валидация, а для других — другая.

J>В DDD есть такие понятия как Entity, Value Object, Aggregate, Aggregate Root.

J>Entity/Value Object — это объекты. Концепция эквивалентности ссылок относится к Entity (User), в то время как структурая эквивалентность — к Value Object(Contact Information). швабра.
J>Aggregate: набор Entities или Value Objects, связанных друг с другом через объект Aggregate Root.
J>Aggregate Root: каждый агрегат имеет корень (в примере выше: User) и границу, агрегатный корень владеет агрегатом и служит шлюзом для всех модификаций внутри агрегата,

J>Бизнес логику желательно писать в самых конечных объектах (Value Objects).

Что? Бизнес логика в ContactInformation? ИмейлАдрес.Отправляйся(), Земля.Копайся() ???

J>В моделях валидацию можно рассматривать как инварианты. Проверка того что объекты всегда в правильном состоянии,

J>весь остальной код в моделях относится только к бизнес логике.
Объекты всегда в правильном состоянии для чего? Для какого процесса?

J>Валидация типа

J>
J>if(repo.GetUserById(id) != null)
J>{
J>    throw new NotFoundException(); 
J>}
J>

J>будет в Application Layer-e.

J>Бизнес логика — это все что говорит заказчик. Сначала определяется уникальный язык(термины) которые заказчик/менеджеры/бизнес аналитик/девы будут использовать чтобы понимать друг друга.

J>Потом договаривают что напр. "Хотим изменить `Info` и `email` у пользователя".
J>Т.к. это относится к сущности User, в моделе User создается метод `UpdateInformation(string info, string email)`, который является прокси методом к бизнес логике,
J>которая будет писаться во вложеных Entitites и Value Objects.

метод UpdateInformation вероятно будет и в анемике тоже. Ключевой поинт анемика лишь в том, нахрена этот метод нужен User-у? Не отменяя того, что User- агрегат для изменения его информации, можно записать
UpdateInformation(User user, string info, string email),
Не говоря уж о том, что можно сделать из этого целый workflow с подтверждением имейла и мобилы, не затрагивая код User-а.

J>Покупатель, корзина товаров, набор акций — это все будут разные агрегаты/агрегат руты.

J>Для взаимодействия агрегатов, что бы проще было расширять бизнес логику и меньше было локов при сохранении данных в бд, в DDD есть понятие как DomainEvents. msdn
J>Напр. у нас есть агрегат руты: User, Order, Product. Пользователь делает заказ.

J>В модель `Order` при создании будет передается 'userId' (Id агрегат рута пользователя) и кидается event создания заказа.

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

Вот, DomainEvents — это такой мост между ричем и анемиком в моем понимании. Т.е. появляется обработчик — это что-то внешнее по отношению к модели данных.
J>
  код
J>полный пример

J>

J>public class Order
J>{
J>  // ..

J>  public Order(string userId, string userName, Address address, int cardTypeId, string cardNumber, string cardSecurityNumber,
J>                string cardHolderName, DateTime cardExpiration, int? buyerId = null, int? paymentMethodId = null) : this()
J>        {
J>            _buyerId = buyerId;
J>            _paymentMethodId = paymentMethodId;
J>            _orderStatusId = OrderStatus.Submitted.Id;
J>            _orderDate = DateTime.UtcNow;
J>            Address = address;

J>            // Add the OrderStarterDomainEvent to the domain events collection 
J>            // to be raised/dispatched when comitting changes into the Database [ After DbContext.SaveChanges() ]
J>            AddOrderStartedDomainEvent(userId, userName, cardTypeId, cardNumber,
J>                                       cardSecurityNumber, cardHolderName, cardExpiration);
J>        }

J>  public void AddOrderItem(int productId, string productName, decimal unitPrice, decimal discount, int units = 1)
J>        {
J>            var existingOrderForProduct = _orderItems.Where(o => o.ProductId == productId)
J>                .SingleOrDefault();

J>            if (existingOrderForProduct != null)
J>            {
J>                //if previous line exist modify it with higher discount  and units..

J>                if (discount > existingOrderForProduct.GetCurrentDiscount())
J>                {
J>                    existingOrderForProduct.SetNewDiscount(discount);
J>                }

J>                existingOrderForProduct.AddUnits(units);
J>            }
J>            else
J>            {
J>                //add validated new order item

J>                var orderItem = new OrderItem(productId, productName, unitPrice, discount, pictureUrl, units);
J>                _orderItems.Add(orderItem);
J>            }
J>        }

J>    // ...

J>     public void SetPaidStatus()
J>        {
J>            if (_orderStatusId == OrderStatus.StockConfirmed.Id)
J>            {
J>                AddDomainEvent(new OrderStatusChangedToPaidDomainEvent(Id, OrderItems));

J>                _orderStatusId = OrderStatus.Paid.Id;
J>                _description = "The payment was performed at a simulated \"American Bank checking bank account ending on XX35071\"";
J>            }
J>        }
J>}
J>


Но здесь обработчик лишь вызывает метод объекта, хотя мог бы иметь такой метод сам. Ну и какой смысл совать все такие методы в объекты?

J>В ApplicationLayer-e мы можем подписаться на все эти события и на каждое событие написать свой обработчик (DomainHandler). github


J>Из примеров проектов которые можно посмотреть:

J>https://github.com/aspnetboilerplate/aspnetboilerplate
J>https://github.com/zkavtaskin/Domain-Driven-Design-Example
J>https://github.com/dotnet-architecture/eShopOnContainers

J>и книга по ddd без воды: "Alexey Zimarev, Hands-On Domain-Driven Design with .NET Core — Tackling complexity in the heart of software by putting DDD principles into practice (2019)"


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