DDD w połączeniu z Entity Framework - dodawanie encji 'dzieci'.

0

Zabieram się na naukę DDD i zastanawiam się czy powinienem robić dwa osobne modele dla EF i dla encji domenowych. Z początku wydało mi się to zbyteczne i zrobiłem wspólny model, jednak mam kilka pytań. Oto moja klasa Order:

    public class Order: EntityBase
    {
        private ICollection<OrderLine> _lines{ get; set; }

        public Order(Guid id, string name)
        {
            Id = id;
            Name = name;
        }

        // Empty constructor for EF
        protected Order() { }

        public string Name { get; private set; }
        public IEnumerable<OrderLine> Lines { get => _lines; }

        public static class Mappings
        {
            public const string OrderLinesCollectionName = nameof(_lines);
            public static Expression<Func<Order, ICollection<OrderLine>>> Orders{ get => x => x._lines; }
        }
    }

oraz model OrderLine:

    public class OrderLine: EntityBase
    {
        protected OrderLine(Guid id, string name)
        {
            Id = id;
            Name = name;
        }

        // Empty constructor for EF
        protected OrderLine() { }

        public string Name { get; private set; }

        public Guid OrderId { get; set; } // co z tym - gdzie to poprawnie ustawiać? 
    }

Encję Order traktuje jako aggregate root, więc chciałbym tam umieścić poniższe funkcję:

  • AddOrderLine(OrderLine line)
  • RemoveOrderLine(OrderLine line)

Mam też implementację IUnitOfWork oraz IRepository dla Order. Chcę, żeby przykładowa logika aplikacji wyglądała:

public void AddOrderLineToOrder(Guid orderId, string orderLineName)
{
	Order order = orderRepo.Get(orderId);
	OrderLine line = OrderLine.Create(orderLineName);
	order.AddOrderLine(line);
	orderRepo.Update(order);

	unitOfWork.Commit();
}

I teraz moje pytania:

  1. gdzie spada odpowiedzialność ustawienia OrderId w encji OrderLine? Zgodnie z DDD pole to powinno być prywatne. Czy w takim razie powinienem dodać funkcę OrderLine.SetOrder(Order order)? Z punku biznesowego to raczej linie są dodawane do Orderu a nie na odwrót.
  2. czy to prawd, że powinienem mieć repozytoria tylko dla aggregate root? Skoro tak, czy IOrderRepository.Update(Order) też powinno aktualizować OrderLine?
  3. czy mój powyższy kod nie łamie jakiś zasad DDD (wiem, że nie ma go za wiele, ale warto wiedzieć już na początku)?

Pozwolę sobie zawołać @somekind :)

3

Odnośnie samej logiki Twojego serwisu aplikacyjnego to ona powinna wyglądać tak

public void AddOrderLineToOrder(Guid orderId, string orderLineName)
{
    unitOfWork.Start();
    // pobieramy nasz agregat z repozytorium, ew inne obiekty domenowe
    Order order = orderRepo.Get(orderId);
    // dodajemy nową pozycję, czyli orkiestrujemy operację na naszej domenie
    order.AddOrderLine(orderLineName);
    // zapisujemy zmiany które zaszły w naszej domenie 
    unitOfWork.Commit();
}

żadnego orderRepo.Update(order);, UoW jest od śledzenia zmian w obiektach domenowych, a dalej w skrócie


    public class Order: EntityBase
    {
        private ICollection<OrderLine> _lines{ get; set; }

        public void AddOrderLine(string orderLineName)
        {
               OrderLine line = OrderLine.Create(orderLineName);
               lines.Add(_lines);
        }       

        (...)
   }

od nadawanie idków, jest ORM/baza

0

@neves: co w przypadku kiedy do stworzenia OrderLine potrzebuje dużo więcej parametrów? Wszystkie je przekazuje z serwisu aplikacyjnego do AddOrderLine() czy wywołuję fabrykę w serwisie aplikacyjnym i dalej przekazuję już gotowy obiekt?

Jak radzicie sobie z sytuacjami kiedy nieraz chcemy pobrać Order od razu z OrderLines a nie raz sam Order? W OrderRepo.Get() dajemy dodatkowy parametr który to załatwia?

3
Mateusz napisał(a):

@neves: co w przypadku kiedy do stworzenia OrderLine potrzebuje dużo więcej parametrów? Wszystkie je przekazuje z serwisu aplikacyjnego do AddOrderLine() czy wywołuję fabrykę w serwisie aplikacyjnym i dalej przekazuję już gotowy obiekt?

Z purystycznego punktu widzenia obiekty agregowane powinny być tworzone wewnątrz agregatu.

Mateusz napisał(a):

Jak radzicie sobie z sytuacjami kiedy nieraz chcemy pobrać Order od razu z OrderLines a nie raz sam Order? W OrderRepo.Get() dajemy dodatkowy parametr który to załatwia?

Z punktu widzenia domeny zawsze pracujemy na kompletnym agregacie (albo iluzji/proxy), więc explicite nie możemy robić takiego rozróżnienia w interfejsie repozytorium.
Natomiast nic nie stoi na przeszkodzie aby się zająć tym problem w warstwie infrastruktury która dostarcza implementacje naszych repozytoriów. I tak jeśli obiekty domenowe pobierane z danej metody repozytorium przeważnie nie potrzebują swoich dzieci, to wtedy polegamy na LazyLoadingu z ORM'a, czyli zwracamy iluzję/proxy kompletnego agregatu. Natomiast jeśli zwykle jest potrzebny kompletny agregat, to robimy eager loading z ORM'a.

0

Ta Encja oraz Agregat wyrażają domenę czy presistance model.?

0

btw. Czy istnieje jakas ksiazka do DDD napisana po ludzku a nie w wersji Evansa i Vernona?
Te ksiazki naprawde są bardzo ciężkostrawne.

1 użytkowników online, w tym zalogowanych: 0, gości: 1