DDD протаскивание других слоев через параметры методов Domain
От: #John Европа https://github.com/ichensky
Дата: 11.11.20 11:12
Оценка:
Здравствуйте,

как вы боритесь с тем, что в методы entity, через параметры, в Domain слой протягиваютя классы из других слоев?


internal class Entity : BaseDomainClass<RootAggregate>
{

  internal Entity(RootAggregate root, int id)
  {
     this.root = root;
     this.id = id;
  }

  internal void DoCool(string value, Ac.System system, ILogger logger, IDateTimeProvider, INumberProvider)
  {
     // domain logic       
     ...
     if(...)
     {
        var data = system.GetData(...);
        logger.Log(...);
     }

     // domain logic 
     ...
  }
}


и в каком-то менеджере:
rootAgreagete.Do("xxx","yyy", this.system1, this.system2, this.system3, this.logger, this.provider1, this.provider2, ....);
Підтримати Україну у боротьбі з країною-терористом.

https://prytulafoundation.org/
https://u24.gov.ua/

Слава Збройним Силам України!!! Героям слава!!!
Re: DDD протаскивание других слоев через параметры методов Domain
От: samius Япония http://sams-tricks.blogspot.com
Дата: 11.11.20 11:44
Оценка: 6 (1) +1
Здравствуйте, #John, Вы писали:

J>Здравствуйте,


J>как вы боритесь с тем, что в методы entity, через параметры, в Domain слой протягиваютя классы из других слоев?


http://rsdn.org/forum/philosophy/4441613.1
Автор: Sinclair
Дата: 03.10.11
Re: Посоветуйте сайты для поиска работы в США, Канада или Германия
От: Gurney Великобритания www.kharlamov.biz
Дата: 11.11.20 12:36
Оценка: -1
Здравствуйте, #John, Вы писали:

J>Здравствуйте,


J>как вы боритесь с тем, что в методы entity, через параметры, в Domain слой протягиваютя классы из других слоев?


ORM и Dependency Injection?
Re: DDD протаскивание других слоев через параметры методов Domain
От: Буравчик Россия  
Дата: 11.11.20 16:16
Оценка: +1
Здравствуйте, #John, Вы писали:

J>как вы боритесь с тем, что в методы entity, через параметры, в Domain слой протягиваютя классы из других слоев?


Выделяю поведение в отдельные классы — бизнес-сервисы. Зависимости прокидываю через конструктор в composition root.
Best regards, Буравчик
Re[2]: DDD протаскивание других слоев через параметры методов Domain
От: #John Европа https://github.com/ichensky
Дата: 20.11.20 22:05
Оценка:
Здравствуйте, Буравчик, Вы писали:

Б>Выделяю поведение в отдельные классы — бизнес-сервисы. Зависимости прокидываю через конструктор в composition root.


Спасибо за ответ, поискал информацию еще в интернете, получается, бизнес-сервисы это один из оптимальных вариантов.
Но и у него есть недостатки: бизнес логика дробится.
Підтримати Україну у боротьбі з країною-терористом.

https://prytulafoundation.org/
https://u24.gov.ua/

Слава Збройним Силам України!!! Героям слава!!!
Re[3]: DDD протаскивание других слоев через параметры методов Domain
От: Буравчик Россия  
Дата: 21.11.20 07:28
Оценка: 1 (1) +1
Здравствуйте, #John, Вы писали:

J>Но и у него есть недостатки: бизнес логика дробится.


И какая проблема из этого возникает (пример)?

Вообще, она всегда дробится. Вопрос лишь как — по сущностям или по сервисам.
Best regards, Буравчик
Re[4]: DDD протаскивание других слоев через параметры методо
От: #John Европа https://github.com/ichensky
Дата: 23.11.20 19:32
Оценка:
Здравствуйте, Буравчик, Вы писали:

J>>Но и у него есть недостатки: бизнес логика дробится.


Б>И какая проблема из этого возникает (пример)?


class Root
{
  // ..

  void Smth()
  {
    bar.DoBar();

    // other logic ..
  }
}

class Bar
{
  // ..

  void DoBar()
  {
        foo.UpdateStatus();

        // other logic ..
  }
}

class Foo
{
  // ..
  private StatusEnum status;

  void UpdateStatus()
  {
    switch(..)
    {
        case "yyy":
            status = StatusEnum.Old;
            break;
        case "xxx": 
            // Если не создавать бизнес сервис,
            // бизнес-логика выглядит последовательной. 
            var x = new HttpClient().GetString("http://...");
            if(x == "str")
            {
                status = StatusEnum.New;
                // other logic ..
            }
        //..
    }
    
    // other logic ..
    
  }
}


Если тут использовать бизнес сервис, то придется часть метода(или весь метод) `UpdateStatus()` вынести в бизнес сервис,
там сделать проверку результата, а потом продолжить выполнение логики из метода "UpdateStatus".

Чем выше будет цикломатическая сложность/вложеных_методов тем сложнее/больше_кода придется вынести в бизнес сервис.

Б>Вообще, она всегда дробится. Вопрос лишь как — по сущностям или по сервисам.

Если выносить код в бизнес сервисы, то данные и способы их обработки оказываются разделены, что нарушает один из принципов ООП,
т.к. не позволяет модели обеспечивать собственные инварианты.
Підтримати Україну у боротьбі з країною-терористом.

https://prytulafoundation.org/
https://u24.gov.ua/

Слава Збройним Силам України!!! Героям слава!!!
Отредактировано 24.11.2020 12:03 #John . Предыдущая версия .
Re[5]: DDD протаскивание других слоев через параметры методов Domain
От: Sinclair Россия https://github.com/evilguest/
Дата: 24.11.20 07:26
Оценка: +1
Здравствуйте, #John, Вы писали:

J>Если тут использовать бизнес сервис, то придется часть метода(или весь метод) `UpdateStatus()` вынести в бизнес сервис,

J>там сделать проверку результата, а потом продолжить выполнение логики из метода "UpdateStatus".
А Foo и Bar у вас что — типы предметной области?
В нормальной архитектуре метод UpdateStatus не является членом класса "данных", а принадлежит сервису.
Поэтому никакого дробления не будет.

J>Чем выше будет цикломатическая сложность/вложеных_методов тем сложнее/больше_кода придется вынести в бизнес сервис.


Б>>Вообще, она всегда дробится. Вопрос лишь как — по сущностям или по сервисам.

J>Если выносить код в бизнес сервисы, то данные и способы их обработки оказываются разделены, что нарушает один из принципов ООП,
J>т.к. не позволяет модели обеспечивать собственные инварианты.
Нет такого принципа.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[6]: DDD протаскивание других слоев через параметры методов Domain
От: #John Европа https://github.com/ichensky
Дата: 24.11.20 16:18
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>А Foo и Bar у вас что — типы предметной области?

Да, Foo, Bar, Root — это модели из предметной области; Root — это агрегат рут.

S>В нормальной архитектуре метод UpdateStatus не является членом класса "данных", а принадлежит сервису.

S>Поэтому никакого дробления не будет.
В примере не дописал код.
UpdateStatus меняет значение переменной `status` у класса Foo:
(`status` — потом буде сохранено в бд)
class Foo
{
  // ..
  private StatusEnum status;

  void UpdateStatus()
  {
    switch(..)
    {
        case "yyy":
            status = StatusEnum.Old;
            break;
        case "xxx": 
            var x = new HttpClient().GetString("http://...");
            if(x == "str")
            {
                status = StatusEnum.New;
                // other logic ..
            }
        //..
    }
    
    // other logic ..
    
  }
}
Підтримати Україну у боротьбі з країною-терористом.

https://prytulafoundation.org/
https://u24.gov.ua/

Слава Збройним Силам України!!! Героям слава!!!
Re[6]: DDD протаскивание других слоев через параметры методов Domain
От: #John Европа https://github.com/ichensky
Дата: 24.11.20 18:15
Оценка:
Здравствуйте, Sinclair, Вы писали:

J>>Если выносить код в бизнес сервисы, то данные и способы их обработки оказываются разделены, что нарушает один из принципов ООП,

J>>т.к. не позволяет модели обеспечивать собственные инварианты.
S>Нет такого принципа.

В посте про амемичную модель Фовлер пишет:

... the basic idea of object-oriented design; which is to combine data and process together ...

Підтримати Україну у боротьбі з країною-терористом.

https://prytulafoundation.org/
https://u24.gov.ua/

Слава Збройним Силам України!!! Героям слава!!!
Re[7]: DDD протаскивание других слоев через параметры методов Domain
От: Sinclair Россия https://github.com/evilguest/
Дата: 25.11.20 10:52
Оценка:
Здравствуйте, #John, Вы писали:

J>В посте про амемичную модель Фовлер пишет:

J>

J>... the basic idea of object-oriented design; which is to combine data and process together ...

Ну, вот за это мы Фаулера и не любим.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[7]: DDD протаскивание других слоев через параметры методов Domain
От: Sinclair Россия https://github.com/evilguest/
Дата: 25.11.20 11:04
Оценка:
Здравствуйте, #John, Вы писали:

S>>В нормальной архитектуре метод UpdateStatus не является членом класса "данных", а принадлежит сервису.

S>>Поэтому никакого дробления не будет.
J>В примере не дописал код.
J>UpdateStatus меняет значение переменной `status` у класса Foo:
J>(`status` — потом буде сохранено в бд)
J>
J>class Foo
J>{
J>  // ..
J>  private StatusEnum status;

J>  void UpdateStatus()
J>  {
J>    switch(..)
J>    {
J>        case "yyy":
J>            status = StatusEnum.Old;
J>            break;
J>        case "xxx": 
J>            var x = new HttpClient().GetString("http://...");
J>            if(x == "str")
J>            {
J>                status = StatusEnum.New;
J>                // other logic ..
J>            }
J>        //..
J>    }
    
J>    // other logic ..
    
J>  }
J>}

J>


Ну всё верно — поэтому-то рич модель плохо работает. Вы втаскиваете внутрь объекта несвойственную ему логику. Он уже вон и в интернет побежал, и всякое прочее делает.
В нормальном дизайне у вас будет что-то вроде FooService.UpdateStatus(foo). Или FooService.UpdateStatus(IEnumerable<Foo> foos).
А уже вот этот FooService будет оборудован каким-нибудь XProvider, который в него инжектируется DI фреймворком или вообще вручную, если нет нужды поддерживать разнообразие конфигураций. Так что вместо new HttpClient().GetString там будет XProvider.GetX()
Это позволит, в частности, тестировать поведение FooService без подключения к интернету и вызова настоящих внешних сервисов.
А также повторно использовать его код в тех случаях, когда у нас есть классы, которые по данным неотличимы от Foo, но с другим поведением. Или вообще — может оказаться, что это тот же самый Foo, только на другом этапе жизненного цикла.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[7]: DDD протаскивание других слоев через параметры методов Domain
От: samius Япония http://sams-tricks.blogspot.com
Дата: 25.11.20 16:48
Оценка: +1
Здравствуйте, #John, Вы писали:

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


J>>>Если выносить код в бизнес сервисы, то данные и способы их обработки оказываются разделены, что нарушает один из принципов ООП,

J>>>т.к. не позволяет модели обеспечивать собственные инварианты.
S>>Нет такого принципа.

J>В посте про амемичную модель Фовлер пишет:

J>

J>... the basic idea of object-oriented design; which is to combine data and process together ...


Есть писатели об обратном.
https://blog.inf.ed.ac.uk/sapm/2014/02/04/the-anaemic-domain-model-is-no-anti-pattern-its-a-solid-design/

In this blog post I will consider the arguments against the ADM, and contend that in some scenarios, the ADM appears be an reasonable choice of design, in terms of adherence to the SOLID principles of Object-Oriented design, established by Robert Martin [3], [4].

Re[8]: DDD протаскивание других слоев через параметры методов Domain
От: #John Европа https://github.com/ichensky
Дата: 25.11.20 19:08
Оценка:
Здравствуйте, samius, Вы писали:

S>Есть писатели об обратном.

S>https://blog.inf.ed.ac.uk/sapm/2014/02/04/the-anaemic-domain-model-is-no-anti-pattern-its-a-solid-design/

Читал перевод этой статьи на швабре. Там много не правды. Напр.

Анемичная модель предметной области лучше поддерживает автоматизированное тестирование.
...

В юнит тестах бизнес логику из БМПО можно легко тестировать и поддерживать если мокать БМПО которые используются в коде.
Потому тесты будут одинаково поддерживаемыми. (Думаю об этом автор даже не подумал, т.к. думает если класс хранит данные, значит его мокать нельзя?).

Если меняются требования к хранилищу данных — в АМПО задача решается путем передачи в PurchaseService из вышестоящего класса служб приложения новой реализации существующего интерфейса репозитория [17], [19], не требуя модификации существующего кода; в БМПО так легко не отделаться, модификация базового класса затронет все классы бизнес-сущностей

Автор статьи перепутал Domain Layer и Application Layer. Когда пишут проект по ДДД, БМПО с бизнес логикой находятся в доменном слое(а не в Application Layer), а репозиторий c доступом к данным — в инфраструктурном слое, Domain Layer не зависит от Infrastructure Layer, зависит только от фреймверка на котором пишется код и вспомогательных компонентов/хелперов. В ДДД мы легко можем заменить напр. `ms sql на cosmodb`, при этом не поменяв ни строчки кода в доменном слое.
Підтримати Україну у боротьбі з країною-терористом.

https://prytulafoundation.org/
https://u24.gov.ua/

Слава Збройним Силам України!!! Героям слава!!!
Re[9]: DDD протаскивание других слоев через параметры методов Domain
От: samius Япония http://sams-tricks.blogspot.com
Дата: 25.11.20 20:03
Оценка:
Здравствуйте, #John, Вы писали:

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


S>>Есть писатели об обратном.

S>>https://blog.inf.ed.ac.uk/sapm/2014/02/04/the-anaemic-domain-model-is-no-anti-pattern-its-a-solid-design/

J>Читал перевод этой статьи на швабре. Там много не правды. Напр.

J>

J>Анемичная модель предметной области лучше поддерживает автоматизированное тестирование.
J>...

J>В юнит тестах бизнес логику из БМПО можно легко тестировать и поддерживать если мокать БМПО которые используются в коде.
J>Потому тесты будут одинаково поддерживаемыми. (Думаю об этом автор даже не подумал, т.к. думает если класс хранит данные, значит его мокать нельзя?).

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

J>

J>Если меняются требования к хранилищу данных — в АМПО задача решается путем передачи в PurchaseService из вышестоящего класса служб приложения новой реализации существующего интерфейса репозитория [17], [19], не требуя модификации существующего кода; в БМПО так легко не отделаться, модификация базового класса затронет все классы бизнес-сущностей

J>Автор статьи перепутал Domain Layer и Application Layer. Когда пишут проект по ДДД, БМПО с бизнес логикой находятся в доменном слое(а не в Application Layer), а репозиторий c доступом к данным — в инфраструктурном слое, Domain Layer не зависит от Infrastructure Layer, зависит только от фреймверка на котором пишется код и вспомогательных компонентов/хелперов. В ДДД мы легко можем заменить напр. `ms sql на cosmodb`, при этом не поменяв ни строчки кода в доменном слое.
Тут в самом деле лукавство, т.к. изолировать часть инфраструктуры за интерфейсом репозитория позволяет и рич модель тоже. Но анемик к этому тяготеет по своей природе, а рич требует специальных усилий, что приводит к образованию тем вроде "DDD протаскивание других слоев через параметры методов Domain".
Re[10]: DDD протаскивание других слоев через параметры методов Domain
От: #John Европа https://github.com/ichensky
Дата: 26.11.20 00:20
Оценка: 8 (1)
Здравствуйте, samius, Вы писали:

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


Допустим у нас есть пользователь у него есть контактная информация и нам нужно добавить
бизнес логику, когда пользователь нажимает кнопку "Save", нам приходят данные: `Info` и `email` и мы их обрабатываем.

В DDD это выглядело бы вот так:

  Domain Layer
    public class User
    {
        public Guid Id { get; private set; }

        public string UserName { get; private set; }

        public string Info { get; private set; }

        public ContactInformation ContactInformation { get; private set; }

        protected internal User() { }

        /// <summary>
        /// Used for loading for ex. from the DB
        /// </summary>
        public static User Create(Guid id, string userName, string info, ContactInformation contactInformation)
        {
            return new User
            {
                Id = id,
                Info = info,
                UserName = userName,
                ContactInformation = contactInformation,
            };
        }

        /// <summary>
        /// Used for creation 
        /// </summary>
        public static User Create(string userName, string info, ContactInformation contactInformation)
        {
            if (string.IsNullOrWhiteSpace(userName))
            {
                throw new ArgumentException();
            }
            // other validation ...

            var id = Guid.NewGuid();

            return Create(id, info, userName, contactInformation);
        }

        public virtual ContactInformation GetContactInformation() => ContactInformation;

        public virtual void UpdateInformation(string info, string email)
        {
            // validation ..

            this.Info = info;
            GetContactInformation().UpdateEmail(email);
        }
    }
    public class ContactInformation
    {
        public Guid Id { get; private set; }

        public string Phone { get; private set; }

        public string Email { get; private set; }

        protected internal ContactInformation() { }

        public static ContactInformation Create(Guid id, string phone, string email) => new ContactInformation
        {
            Id = id,
            Phone = phone,
            Email = email
        };

        public static ContactInformation Create(string phone, string email)
        {
            var id = Guid.NewGuid();
            return Create(phone, email);
        }

        public static ContactInformation Create(string email) => Create(null, email);

        internal virtual void UpdateEmail(string newEmail)
        {
            if (newEmail.Contains("admin"))
            {
                throw new ArgumentException();
            }

            this.Email = newEmail;
        }
    }


  Infrastructure Layer
    public class UserRepository
    {

        public User Load(Guid id)
        {
            // Loading from the DB ...
            var contactInformation = ContactInformation.Create(Guid.NewGuid(), "bob@example.com", "+123456");
            var user = User.Create(id, "Bob", "Smth", contactInformation);
            return user;
        }
        public void Save(User user)
        {

        }
    }


  Application Layer
   public class UserInformationDto
    {
        public string Email { get; set; }
        public string Info { get; set; }
    }

    public class UserService
    {

        // IUserRepository
        private UserRepository userRepository;

        public UserService(UserRepository userRepository)
        {
            this.userRepository = userRepository;
        }

        public void UpdateUserInformation(Guid userId, UserInformationDto userInformation)
        {

            // validation ..

            var user = userRepository.Load(userId);

            user.UpdateInformation(userInformation.Info, userInformation.Email);

            userRepository.Save(user);
        }
    }


В трехслойной/четырехслойной архитектуре с анемичными моделями, нам точно так уже пришлось бы мокать `IChangeContactInformationService`
и метод "GetContactInformation". Потому разницы в сложности поддержания/написания тестов — нет.

  Unit tests
public class UserTests
    {
        private User user;
        private ContactInformation contactInformation;

        public UserTests()
        {
            contactInformation = Mock.Of<ContactInformation>();
            user = Mock.Of<User>();
        }

        [Fact]
        public void UpdateInformationTest()
        {
            // Arrange
            var email = "bob2@example.com";
            Mock.Get(user).Setup(x => x.GetContactInformation()).Returns(contactInformation);

            // Act
            var ex = Record.Exception(() => user.UpdateInformation("xxx", email));

            // Assert
            Assert.Null(ex);
        }
    }


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

Из дополнительных плюсов с DDD мы легко можем применить CQRS подход.
Підтримати Україну у боротьбі з країною-терористом.

https://prytulafoundation.org/
https://u24.gov.ua/

Слава Збройним Силам України!!! Героям слава!!!
Re[11]: DDD протаскивание других слоев через параметры методов Domain
От: samius Япония http://sams-tricks.blogspot.com
Дата: 26.11.20 18:07
Оценка: +1
Здравствуйте, #John, Вы писали:

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


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


J>Допустим у нас есть пользователь у него есть контактная информация и нам нужно добавить

J>бизнес логику, когда пользователь нажимает кнопку "Save", нам приходят данные: `Info` и `email` и мы их обрабатываем.

J>В DDD это выглядело бы вот так:


Удивительно рафинированно-анемичный получился пример DDD. Я не вижу в нем ничего, что нельзя было бы выполнить на голом DTO, за исключением, разве что, комментариев
// validation ..
PurchaseItem из статьи был куда более типичен для DDD.
А так же исходный
internal void DoCool(string value, Ac.System system, ILogger logger, IDateTimeProvider, INumberProvider)

или Foo.UpdateStatus с заходом в HttpClient.

J>В трехслойной/четырехслойной архитектуре с анемичными моделями, нам точно так уже пришлось бы мокать `IChangeContactInformationService`

J>и метод "GetContactInformation". Потому разницы в сложности поддержания/написания тестов — нет.

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

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

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

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

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

J>Из дополнительных плюсов с DDD мы легко можем применить CQRS подход.

Сорян, я не вижу причин, почему мы не можем легко применить CQRS подход с анемиком.
Re[12]: DDD протаскивание других слоев через параметры методов Domain
От: #John Европа https://github.com/ichensky
Дата: 26.11.20 22:10
Оценка:
Здравствуйте, samius, Вы писали:

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

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

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

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

В DDD есть такие понятия как Entity, Value Object, Aggregate, Aggregate Root.
Entity/Value Object — это объекты. Концепция эквивалентности ссылок относится к Entity (User), в то время как структурая эквивалентность — к Value Object(Contact Information). швабра.
Aggregate: набор Entities или Value Objects, связанных друг с другом через объект Aggregate Root.
Aggregate Root: каждый агрегат имеет корень (в примере выше: User) и границу, агрегатный корень владеет агрегатом и служит шлюзом для всех модификаций внутри агрегата,
т.е. из Application Layer нельзя написать код:
var contactInformation = new ContactInformation(...);
repo.Save(contactInformaion);

Все изменения нужно делать только через агрегат рут(User):
var user = userRepository.Load(userId);
user.UpdateInformation(userInformation.Info, userInformation.Email);
userRepository.Save(user);


Бизнес логику желательно писать в самых конечных объектах (Value Objects).
В моделях валидацию можно рассматривать как инварианты. Проверка того что объекты всегда в правильном состоянии,
весь остальной код в моделях относится только к бизнес логике.
Валидация типа
if(repo.GetUserById(id) != null)
{
    throw new NotFoundException(); 
}

будет в Application Layer-e.

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

--

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

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


public class Order
{
  // ..

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

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

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

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

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

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

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

    // ...

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

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

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


Из примеров проектов которые можно посмотреть:
https://github.com/aspnetboilerplate/aspnetboilerplate
https://github.com/zkavtaskin/Domain-Driven-Design-Example
https://github.com/dotnet-architecture/eShopOnContainers

и книга по 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)"
Підтримати Україну у боротьбі з країною-терористом.

https://prytulafoundation.org/
https://u24.gov.ua/

Слава Збройним Силам України!!! Героям слава!!!
Re[13]: DDD протаскивание других слоев через параметры методов Domain
От: alexsoff Россия  
Дата: 27.11.20 17:13
Оценка:
Здравствуйте, #John, Вы писали:

J>и

[]
Все споры anemic vs rich можно закончить примером наблюдений за новыми языками и обновлениями старых. Почти везде основной упор идет на фишки ФП. Так что anemic, а именно ее можно сделать через ФП, побеждает сейчас.
Re[14]: DDD протаскивание других слоев через параметры методов Domain
От: #John Европа https://github.com/ichensky
Дата: 27.11.20 22:04
Оценка:
Здравствуйте, alexsoff, Вы писали:

A>Все споры anemic vs rich можно закончить примером наблюдений за новыми языками и обновлениями старых. Почти везде основной упор идет на фишки ФП. Так что anemic, а именно ее можно сделать через ФП, побеждает сейчас.


Оставлю для дополнительного размышления ссылку на статью "Про Anemic Domain Model" . В принципе там все сказано.
Підтримати Україну у боротьбі з країною-терористом.

https://prytulafoundation.org/
https://u24.gov.ua/

Слава Збройним Силам України!!! Героям слава!!!
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.