DDD i generyczne repozytorium

0

W sieci natknąłem się na wpisy dotyczace ddd i wzorca repository.
Otóż czy w tym podejsciu implementacja generycznego repozytorium dla prostych crudów: Find, FindById, Remove, Update jest błędne?
Nawet ktoś nazwał to antywzorcem.
Tworzyć repozytorium per byt, wyspecyfikowane etc...
Z drugiej strony generyczne repozytorium oszczędza duplikację kodu. Z trzeciej 😀 strony pisząc np. w Springu repozytorium wygodnie dziedziczymy po CrudRepository lub lepiej, bo np. z paginacją po JpaRepository.

0

Jeśli będziesz miał powtarzające się zapytania to nie ma w tym nic złego- jak sam napisałeś uniknie to duplikacji kodu. Możesz mieć projekty/solucje dla każdego bounded context oraz bibliotekę infrastrukturalną w której możesz umieścić generyczne repozytorium.

0
Uczynny Terrorysta napisał(a):

W sieci natknąłem się na wpisy dotyczace ddd i wzorca repository.
Otóż czy w tym podejsciu implementacja generycznego repozytorium dla prostych crudów: Find, FindById, Remove, Update jest błędne?

Tak, bo prostych CRUDów nie implementuje się przy pomocy DDD, więc nie ma sensu do nich używać repozytoriów.

Nawet ktoś nazwał to antywzorcem.
Tworzyć repozytorium per byt, wyspecyfikowane etc...

Tak, bo w przeciwnym razie to nie jest repozytorium.

Z drugiej strony generyczne repozytorium oszczędza duplikację kodu.

Nie jeśli sam jest wrapperem na ORMa, bo wtedy to duplikacja kodu.

Z trzeciej 😀 strony pisząc np. w Springu repozytorium wygodnie dziedziczymy po CrudRepository lub lepiej, bo np. z paginacją po JpaRepository.

A jaki to ma związek z C# i DDD?

Generyczne repozytorium w DDD może mieć sens tylko jako bazowa klasa dla konkretnych repozytoriów, które są używane przez serwisy biznesowe. W DDD generyczne repozytorium nie może wyciekać do logiki biznesowej.

0

bo np. z paginacją po JpaRepository.

Dawno czegoś zabawniejszego nie słyszałem, hahaha....

Które są używane przez serwisy biznesowe.

Chyba aplikacyjne, z case'mi biznesowymi...

Generyczne repozytorium w DDD może mieć sens tylko jako bazowa klasa dla konkretnych repozytoriów, które są używane przez serwisy biznesowe. W DDD generyczne repozytorium nie może wyciekać do logiki biznesowej..

Nie zgadzam się, można użyć mechanizmu refleksji do ustawiania prymitywów w konstruktorze Domain Model na poziomie infrastruktury (Repository).

0

ale czy przy założeniu, ze dany projekt realizowany jest w DDD czy

public interface IReadOnlyRepository
{
    IEnumerable<TEntity> GetAll<TEntity>(
        Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
        string includeProperties = null,
        int? skip = null,
        int? take = null)
        where TEntity : class, IEntity;

    Task<IEnumerable<TEntity>> GetAllAsync<TEntity>(
        Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
        string includeProperties = null,
        int? skip = null,
        int? take = null)
        where TEntity : class, IEntity;

    IEnumerable<TEntity> Get<TEntity>(
        Expression<Func<TEntity, bool>> filter = null,
        Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
        string includeProperties = null,
        int? skip = null,
        int? take = null)
        where TEntity : class, IEntity;

    Task<IEnumerable<TEntity>> GetAsync<TEntity>(
        Expression<Func<TEntity, bool>> filter = null,
        Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
        string includeProperties = null,
        int? skip = null,
        int? take = null)
        where TEntity : class, IEntity;

    TEntity GetOne<TEntity>(
        Expression<Func<TEntity, bool>> filter = null,
        string includeProperties = null)
        where TEntity : class, IEntity;

    Task<TEntity> GetOneAsync<TEntity>(
        Expression<Func<TEntity, bool>> filter = null,
        string includeProperties = null)
        where TEntity : class, IEntity;

    TEntity GetFirst<TEntity>(
        Expression<Func<TEntity, bool>> filter = null,
        Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
        string includeProperties = null)
        where TEntity : class, IEntity;

    Task<TEntity> GetFirstAsync<TEntity>(
        Expression<Func<TEntity, bool>> filter = null,
        Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
        string includeProperties = null)
        where TEntity : class, IEntity;

    TEntity GetById<TEntity>(object id)
        where TEntity : class, IEntity;

    Task<TEntity> GetByIdAsync<TEntity>(object id)
        where TEntity : class, IEntity;

    int GetCount<TEntity>(Expression<Func<TEntity, bool>> filter = null)
        where TEntity : class, IEntity;

    Task<int> GetCountAsync<TEntity>(Expression<Func<TEntity, bool>> filter = null)
        where TEntity : class, IEntity;

    bool GetExists<TEntity>(Expression<Func<TEntity, bool>> filter = null)
        where TEntity : class, IEntity;

    Task<bool> GetExistsAsync<TEntity>(Expression<Func<TEntity, bool>> filter = null)
        where TEntity : class, IEntity;

Oraz

public interface IRepository : IReadOnlyRepository
{
    void Create<TEntity>(TEntity entity, string createdBy = null)
        where TEntity : class, IEntity;

    void Update<TEntity>(TEntity entity, string modifiedBy = null)
        where TEntity : class, IEntity;

    void Delete<TEntity>(object id)
        where TEntity : class, IEntity;

    void Delete<TEntity>(TEntity entity)
        where TEntity : class, IEntity;

    void Save();

    Task SaveAsync();
}

Wiesz chodzi mi o to, że tego typu operacje to fundament każdej aplikacji. Jakaś spora cześć logiki będzie potrzebowała takich danych.

1

Spora część logiki potrzebuje czegoś takiego jak GetAll? Śmiem twierdzić, że to jest w ogóle zbędne. Podobnie GetFirst. Delete też zazwyczaj nie ma sensu.
Logika biznesowa nie może też nic wiedzieć o takich szczegółach implementacji jak IQueryable, IOrderedQueryable. No i po co tam te skip i take? Repozytoria nie służą do paginacji.

0

Szybkie Google poszło do dyskusji aby było coś uniwersalnego.
OK, getAll z logiką nie ma nic wspólnego ale zgodzisz się ze tego typu metody dotyczą niemal każdej encji.
Co wtedy? Takie coś nie ląduje w ogóle w danym repozytorium? Dalej czy może używamy EF bezpośrednio w kontrolerze?
Tak to powyżej to wrapper na ORM, wiem, że są opinie, które są against z kolei.

0

ale czy przy założeniu, ze dany projekt realizowany jest w DDD

Jak możesz zakładać, że jest realizowany w DDD skoro nie masz pojęcia co to jest.

Przeczytaj niebieską i czerwoną książkę,
W czerwonej jest pokazana implementacja repo, radzę się nią zasugerować a nie poradami Zosisamosi.

0

Repozytoriów nie tworzy się dla encji tylko dla aggregate rootów. I nie, nie używamy EF w kontrolerach.
Wyświetlanie listy rekordów to nie jest przypadek użycia, w którym podejście DDD miałoby jakikolwiek sens, więc nie ma sensu rozważać jak dopasować do tego repozytorium.

0
  • Jak przekonać ludzi do DDD?
  • Czy DDD tylko nadaje sie do skomplikowanych domen? Czy moze pewne elementy warto uzyc tez w tych mniejszych?
  • kiedy absolutnie nie warto w to isc?
0
Biały Mleczarz napisał(a):
  • Jak przekonać ludzi do DDD?

Ogniem i mieczem. ;)

  • Czy DDD tylko nadaje sie do skomplikowanych domen? Czy moze pewne elementy warto uzyc tez w tych mniejszych?

Zależy jak na to spojrzeć, bo w moim odczuciu DDD to tak naprawdę potwórzone dobre zasady i praktyki OOP: (głównie enkapsulacja i SRP). Natomiast w wielu przypadkach tworzenie tych wszystkich bytów: encji, serwisów, repozytoriów to jest przeinżynierowanie.

  • kiedy absolutnie nie warto w to isc?

W CRUDach i proxy.

0

Co to jest proxy ?

0

Inaczej fasada, serwis który nie ma swojej logiki biznesowej tylko daje dostęp innych systemów.

0

mnie troche rozwala jak ludzie opowiadaja o tych agregatach, event sourcing, sagach, cudach wiankach jakby to niby proste było...

0
Biały Mleczarz napisał(a):

mnie troche rozwala jak ludzie opowiadaja o tych agregatach, event sourcing, sagach, cudach wiankach jakby to niby proste było...

Ja tam mam uprzedzenie do event sourcing.

Saga, Event Sourcing to może wydawać się trudnę. Cała reszta jest prosta, za trzecim razem jak czytasz tą samą książkę ;)

0
Zimny Młot napisał(a):
Biały Mleczarz napisał(a):

mnie troche rozwala jak ludzie opowiadaja o tych agregatach, event sourcing, sagach, cudach wiankach jakby to niby proste było...

Ja tam mam uprzedzenie do event sourcing.

Saga, Event Sourcing to może wydawać się trudnę. Cała reszta jest prosta, za trzecim razem jak czytasz tą samą książkę ;)

Sama idea snapshotowania domain objectow - bez czego obyc sie nie da wydaje sie trudne.

0
Biały Mleczarz napisał(a):
[Zimny Młot napisał(a)rs.net/Forum/1480742):
[Biały Mleczarz napisał(a)]480733):

mnie troche rozwala jak ludzie opowiadaja o tych agregatach, event sourcing, sagach, cudach wiankach jakby to niby proste było...

Ja tam mam uprzedzenie do event sourcing.

Saga, Event Sourcing to może wydawać się trudnę. Cała reszta jest prosta, za trzecim razem jak czytasz tą samą książkę ;)

Sama idea snapshotowania domain objectow - bez czego obyc sie nie da wydaje sie trudne.

Domain Object'y to dla mnie esencja modelowania OOP. Jak to zrozumiesz, możesz Od razu skoczyć do repozytoriów i serwisów applikacyjnych. Mogę ci polecić artykuły Sobutki jak i jego wykłady, na początek, chociaż i tak nic ci tego nie da co przerobienie książek.

0
Zimny Młot napisał(a):
Biały Mleczarz napisał(a):
[Zimny Młot napisał(a)rs.net/Forum/1480742):
[Biały Mleczarz napisał(a)]480733):

mnie troche rozwala jak ludzie opowiadaja o tych agregatach, event sourcing, sagach, cudach wiankach jakby to niby proste było...

Ja tam mam uprzedzenie do event sourcing.

Saga, Event Sourcing to może wydawać się trudnę. Cała reszta jest prosta, za trzecim razem jak czytasz tą samą książkę ;)

Sama idea snapshotowania domain objectow - bez czego obyc sie nie da wydaje sie trudne.

Domain Object'y to dla mnie esencja modelowania OOP. Jak to zrozumiesz, możesz Od razu skoczyć do repozytoriów i serwisów applikacyjnych. Mogę ci polecić artykuły Sobutki jak i jego wykłady, na początek, chociaż i tak nic ci tego nie da co przerobienie książek.

Ale jest sens tak na sucho sie uczyc? Bo ja odnioslem wrazenie, ze bedzie mialo to sens jak kiedys trafie do takiego projektu...

1

Bo jest pewien próg wejścia w implementację "takich rzeczy". Abstrakcyjnie wg opisów książek to jest ok ale implementacja wymaga umiejętności i pewnej biegłości w programowaniu.
Jak się myśli poprzez UI to wyjdzie zwykle encja na twarz i pchasz.

Edit
Dodam jeszcze TDD jako Table Driven Design. Ktoś u Aniserowicza tak to zdefiniowal w kontekście php.

0

Generyczne repozytorium jak najbardziej tak, jednak w moim przypadku jest ono sprowadzone do minimum.

  public interface IRepository<T>
    where T : AggregateRoot
  {
    Task SaveAsync(T aggregate, CancellationToken cancellationToken);
    Task<T> GetByIdAsync(Guid id, CancellationToken cancellationToken);
  }

Przez to, że logika biznesowa jest zazwyczaj w agregatach / encjach / serwisach, takie repo sprawdza się w większości przypadków. Zazwyczaj potrzebujesz pobrać z bazy aggregate roota, wykonać na nim metody (logikę biznesową) i ponownie zapisać.

Co do tego, że DDD nie nadaje się do CURDów, to tutaj bym polemizował. Tworzymy aplikację w całości w DDD, jako że chyba w każdym projekcie są przypadki z mniej skomplikowaną logiką (typowy CRUD), a jednak chcemy mieć spójność w obrębie całego projektu, to DDD używamy również tam. Zauważyliśmy, że nawet w CRUDAch powstaje mniej kodu, odcinamy się od bibliotek zewnętrznych (Automapper, FluentValidation), są mniejsze problemy z zachowaniem spójności modelu.

0

Co do tego, że DDD nie nadaje się do CURDów, to tutaj bym polemizował. Tworzymy aplikację w całości w DDD, jako że chyba w każdym projekcie są przypadki z mniej skomplikowaną logiką (typowy CRUD), a jednak chcemy mieć spójność w obrębie całego projektu, to DDD używamy również tam. Zauważyliśmy, że nawet w CRUDAch powstaje mniej kodu, odcinamy się od bibliotek zewnętrznych (Automapper, FluentValidation), są mniejsze problemy z zachowaniem spójności modelu.

Co Automapper ma z zachowaniem spójności modelu i niby gdzie ty go chcesz używać??

(typowy CRUD), a jednak chcemy mieć spójność w obrębie całego projektu, to DDD używamy również tam

Widze, że dla ciebie DDD to tylko 4 warstwy gdzie domenowa musi implementować Domain Model jak dla większości ludzi, którzy nie mają o DDD pojęcia.
Największym atutem DDD są konteksty. Niestety większość ludzi chyba przegapiła ten rozdział w książce ablo w ogóle jej nie otworzyła i uczyła się z jakiś tutoriali do DDD albo blogów.

DDD nie mówi, że w każdym kontekście aplikacji musisz wszsystko modelować w DM. Lecz jeśli główna (core) domena nie potrzebuje DM to i DDD tam nie pasuje. Jak jest w przypadku Crudów.

Możesz mieć generyczny kontekst Crudowy ("pomocniczy") oparty o np. generyczny TransactionSript bez repo i warstwy applikacyjnej (bo niby po co ci jedno i drugie) oraz drugi kontekst do rozwiązywania problemów konkretnej domeny z Domain Model.

Co do tego, że DDD nie nadaje się do CURDów, to tutaj bym polemizował.

Jak wpadniez z połówką i zakąską to mogę z tobą o tym polemizować ale tak na sucho to nie widzę sensu.

0

A umie ktos powiedziec jakie duze firmy uzywaja DDD?

1

@karsa: Duża firmy nie umieją :)

Taki żart, bo mnie tu zaraz zjedzą.

0
karsa napisał(a):

A umie ktos powiedziec jakie duze firmy uzywaja DDD?

Nie ma czegoś takiego, że firma DDD używa. To ludzie używają narzędzi, więc może gdzieś tam w jakichś firmach są zespoły, które tak działają. Może nawet ze zrozumieniem i może nawet wychodzi to sensownie.

0

Mam takie pytanie dotyczące DDD. A mianowicie:

1.Mając jakąś encję będącą AggregateRoot-em i do niej repozytorium to z tego co wyczytałem repozytorium nie powinno zawierać metod typu Save() czy Update(), a tym powinien zajmować się UnitOfWork wykorzystując np.: metodą Commit(). Pytanie moje brzmi w jaki sposób UnitOfWork ma wiedzieć o zmianach zachodzących w obiekcie domenowym?. Używając w warstwie DAL np.: Dapper-a nie mamy możliwości śledzenia zmian. A nawet biorąc pod uwagę EF to przecież obiekt domenowy ma różne właściwości od obiektów DAL.

  1. W jaki sposób poprawnie powinna być wykonana operacja aktualizacji (w aplikacji WebApi2 z zastosowaniem DDD) np.: dla zamówienia mającego poszczególne pozycje oraz np.: pole Uwagi. Taki scenariusz: Użytkownik wchodzi do edycji takie zamówienia z UI, zmienia załóżmy Opis i klika "Zapisz". Obiekt DTO jest wysyłany do Controllera. Co powinno nastąpić w dalszej częsci? Powinniśmy realizować logikę domenową czy przeprowadzić operacje na DAL za pomocą jakis serwisów? To drugie rozwiązanie troche się mija z celem, a znowu to pierwsze wg. mnie wymusza, że obiekt domenowy powinien mieć metody mogące zmieniać każdą własciwość tego obiektu.
0
[moneusz napisał(a)]et/Forum/1482061):

Mam takie pytanie dotyczące DDD. A mianowicie:

1.Mając jakąś encję będącą AggregateRoot-em i do niej repozytorium to z tego co wyczytałem repozytorium nie powinno zawierać metod typu Save() czy Update(), a tym powinien zajmować się UnitOfWork wykorzystując np.: metodą Commit(). Pytanie moje brzmi w jaki sposób UnitOfWork ma wiedzieć o zmianach zachodzących w obiekcie domenowym?. Używając w warstwie DAL np.: Dapper-a nie mamy możliwości śledzenia zmian. A nawet biorąc pod uwagę EF to przecież obiekt domenowy ma różne właściwości od obiektów DAL.

  1. W jaki sposób poprawnie powinna być wykonana operacja aktualizacji (w aplikacji WebApi2 z zastosowaniem DDD) np.: dla zamówienia mającego poszczególne pozycje oraz np.: pole Uwagi. Taki scenariusz: Użytkownik wchodzi do edycji takie zamówienia z UI, zmienia załóżmy Opis i klika "Zapisz". Obiekt DTO jest wysyłany do Controllera. Co powinno nastąpić w dalszej częsci? Powinniśmy realizować logikę domenową czy przeprowadzić operacje na DAL za pomocą jakis serwisów? To drugie rozwiązanie troche się mija z celem, a znowu to pierwsze wg. mnie wymusza, że obiekt domenowy powinien mieć metody mogące zmieniać każdą własciwość tego obiektu.

Góra powinna gadać z serwisem w warstwie App. Strasznie uparty jesteś i narwany. ;)
Dowiedz się do czego używa się DTO (z DTO zaczyna się robi taka sama plaga jak Repository).
Dowiedz się jak ORM śledzi zmiany w obiektach i zacznij czytać dobre książki, a nie pierdoły na forum. ;)

0

Rozumiem, że góra miałeś na myśli Controller w tym wypadku? Jeśli tak to tak właśnie mam to zaprojektowane.
Jeśli chodzi o DTO to używam tych obiektów jako struktury danych (do wymiany pomiędzy UI a Controllerem). Czy nie do tego one służą?
Jeśli chodzi o ORM i śledzenie zmian to tak jak pisałem używam Dapper-a na tą chwilę i niechciał bym tego zmieniać, a przecież korzystanie z podejścia DDD tego nie wyklucza.

Problemy, które widzę opisałem w pkt.1 i 2. Być może to nie są problemy albo muszę coś przeprojektować.

1

Jeali model zaklada mozliwosc dodania uwag to IMHO aggtegateroot (AR) powinien miec metode do realizacji takiej operacji. I nie do zmiany kazdej wlasciwosci tylko do tego co wynika z procesu, z logiki biznesowej.

0
jacek.placek napisał(a):

Jeali model zaklada mozliwosc dodania uwag to IMHO aggtegateroot (AR) powinien miec metode do realizacji takiej operacji. I nie do zmiany kazdej wlasciwosci tylko do tego co wynika z procesu, z logiki biznesowej.

Dobrze powiedziane.

Jak do Struktury Mapowania dopisujesz DTO. To tak jak byś chciał zaznaczyć, że jest to obiekt zbiorczy z kilku metod lub ma on zmiejszyć ilość requestów do API po HTTP.

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