2010-04-03 7 views
10

Wystarczy stworzył acc na SO zapytać to :)W podejściu DDD, czy ten przykład jest poprawnie modelowany?

Zakładając ten uproszczony przykład: budowania aplikacji internetowej do zarządzania projektami ...
Aplikacja posiada następujące wymogi/zasady.

1) Użytkownicy powinni mieć możliwość tworzenia projektów wstawiających nazwę projektu.
2) Nazwy projektów nie mogą być puste.
3) Dwa projekty nie mogą mieć tej samej nazwy.

Używam architektury 4-warstwowej (interfejs użytkownika, aplikacja, domena, infrastruktura).
Na moim warstwie aplikacji mam następujące klasy ProjectService.cs:

public class ProjectService 
{ 
    private IProjectRepository ProjectRepo { get; set; } 

    public ProjectService(IProjectRepository projectRepo) 
    { 
     ProjectRepo = projectRepo; 
    } 

    public void CreateNewProject(string name) 
    { 
     IList<Project> projects = ProjectRepo.GetProjectsByName(name); 
     if (projects.Count > 0) throw new Exception("Project name already exists."); 

     Project project = new Project(name); 
     ProjectRepo.InsertProject(project); 
    } 
} 

Na mojej domenie warstwy, mam klasę Project.cs oraz interfejs IProjectRepository.cs:

public class Project 
{ 
    public int ProjectID { get; private set; } 
    public string Name { get; private set; } 

    public Project(string name) 
    { 
     ValidateName(name); 
     Name = name; 
    } 

    private void ValidateName(string name) 
    { 
     if (name == null || name.Equals(string.Empty)) 
     { 
      throw new Exception("Project name cannot be empty or null."); 
     } 
    } 
} 




public interface IProjectRepository 
{ 
    void InsertProject(Project project); 
    IList<Project> GetProjectsByName(string projectName); 
} 

na moim Warstwa infrastruktury, mam implementację IProjectRepository, która wykonuje faktyczne zapytania (kod jest nieistotny).


nie podoba mi się dwie rzeczy o tym wzorem:

1) Czytałem, że repozytorium interfejsy powinny być częścią domeny, ale implementacje nie powinno. To nie ma dla mnie sensu, ponieważ uważam, że domena nie powinna wywoływać metod repozytorium (ignorancja uporczywości), która powinna być odpowiedzialnością usług w warstwie aplikacji. (Coś mi mówi, że się mylę).

2) Proces tworzenia nowego projektu wymaga dwóch walidacji (nie zerowych i nie duplikowanych). W moim projekcie powyżej te dwa walidacje są rozproszone w dwóch różnych miejscach, co utrudnia (imho) obserwowanie, co się dzieje.

Moje pytanie brzmi, czy z perspektywy DDD jest to prawidłowo modelowane, czy zrobiłbyś to w inny sposób?

Odpowiedz

1

Myślę, że częścią zamieszania z (1) jest to, że brakuje ci warstwy - włóż warstwę usług do swojej architektury, a twój problem zniknie jak magia. Usługę i implementację repozytorium można umieścić w warstwie usługi - tzn. Użytkownik korzysta z usługi korzystającej z konkretnej implementacji repozytorium. Inne usługi mogą dowolnie wybrać alternatywną implementację repozytorium. Twoja aplikacja może dowolnie wybierać dowolny interfejs serwisu, który lubi. Powiedziawszy to, nie jestem pewien, czy to naprawdę ma znaczenie w większości przypadków. W prawie wszystkich moich aplikacjach mam jedną "domenę/datalayer", która zasadniczo została naprawiona. Mogę osadzić na nim repozytorium, zależnie od tego, jak skomplikowana jest logika biznesowa. To samo dotyczy usługi - może nie być konieczne, jeśli projekt nie jest bardzo skomplikowany. Jeśli stanie się tak później, zawsze mogę zrestrukturyzować. Zazwyczaj umieszczam moje repozytorium w tym samym projekcie, co mój kontekst danych (przy użyciu LINQ), a jeśli byłaby to usługa, byłby to osobny projekt (ponieważ zazwyczaj byłby również wyświetlany jako usługa sieciowa).

W odniesieniu do (2) trzeba pomyśleć o problemie z perspektywy współbieżności. Weryfikacja duplikatu nazwy najlepiej jest wykonać za pomocą ograniczenia bazy danych, jeśli to możliwe. Myślę, że to najprostszy sposób na wymuszenie tej logiki. Możesz oczywiście sprawdzić, czy istnieje duplikat przed próbą wstawienia, ale dopóki nie wykonasz tego w ramach transakcji, nie możesz zagwarantować, że inny proces nie pojawi się i wstawić między czekiem a wkładką.Ograniczenie bazy danych rozwiązuje ten problem. Przeniesienie sprawdzenia do logiki wstawiania (ta sama transakcja) również rozwiązuje problem, ale niezależnie od tego, myślę, że musisz być przygotowany na to, aby obsłużyć go jako błąd wstawienia, a także (lub zamiast) błąd sprawdzania poprawności.

+0

Jeśli chodzi o (1), myślę, że moja warstwa aplikacji mogłaby służyć do celów warstwy serwisowej, gdybym miał kolejną warstwę, myślę, że mógłbym skończyć z warstwami bez znaczenia i odpowiedzialności dzielonych między warstwami. Kiedy mówisz o złożoności projektu, całkowicie się z Tobą zgadzam. W większości moich aplikacji używam podejścia "Wzorzec aktywnej rekordy", a ten wzór może wydawać się bardziej odpowiedni do rozwiązania tego przykładu. Celowo opuściłem ten przykład uproszczony, ale próbuję dowiedzieć się, jak poprawnie modelować przy użyciu wzorca repozytorium, dlatego odchylam się od wzoru AR. – Tag

+0

Jeśli chodzi o (2), twoja sugestia ma dla mnie sens i myślę, że jest to rzeczywiście najlepsza opcja w tym przypadku. – Tag

2

Proces tworzenia nowego projektu wymaga dwóch walidacji (nie zerowych i nie duplikowanych). W moim projekcie powyżej te dwa walidacje są rozproszone w dwóch różnych miejscach, co utrudnia (imho) obserwowanie, co się dzieje.

Projekt nie może i nie powinien być świadomy wszystkich projektów w aplikacji (sam element nie powinien być świadomy wszystkich innych produktów na liście), w związku z tym. - to jest obowiązkiem służby domeny (instead of application service sprawdzić Evansa książkę, aby zrozumieć dokładną różnicę).

Istnieje wiele rodzajów sprawdzania poprawności. I there can't be universal validation mechanism. DDD mówi tylko, że musisz wprowadzić walidację domeny w modelu domeny.