🔥 Zapisy zamknięte, ale możesz pobrać Roadmapę .NET i dołączyć do listy oczekujących — Pobierz i dołącz do Listy VIP →

DDD (Domain-Driven Design) w C# — building blocks na przykładach

Domain-Driven Design (Eric Evans, 2003) to podejście, w którym kod modeluje realny język i reguły biznesu, nie tylko tabele w bazie danych. Zamiast zaczynać projektowanie od schematu SQL, zaczynasz od pytania: jak eksperci biznesowi nazywają te pojęcia i jakie mają reguły? Poniżej — pięć podstawowych “klocków” DDD, każdy na realnym kodzie C#.

Ubiquitous Language — wspólny język w kodzie

Nazwy klas i metod powinny brzmieć dokładnie tak, jak mówi o nich ekspert biznesowy — nie jak brzmiałoby to wygodniej dla programisty.

// ❌ Nazwy techniczne, oderwane od języka biznesu
public class OrderProcessor { public void Process(OrderDto dto) { } }

// ✅ Nazwy, których użyłby ekspert od sprzedaży
public class Zamowienie
{
    public void ZlozZamowienie() { }
    public void Anuluj() { }
    public void OznaczJakoWyslane() { }
}

Gdy kod i rozmowa z biznesem używają tych samych słów, tłumaczenie między “tym, co chce biznes” a “tym, co robi kod” znika — to jeden z głównych powodów, dla których DDD w ogóle istnieje.

Entity — tożsamość ważniejsza niż wartości pól

public class Zamowienie // Entity — ma tożsamość (Id), która nie zmienia się przez cały cykl życia
{
    public int Id { get; }
    public string Status { get; private set; } = "Nowe";

    public Zamowienie(int id) => Id = id;

    public void OznaczJakoWyslane() => Status = "Wysłane";
}

var a = new Zamowienie(5);
var b = new Zamowienie(5);
// a i b reprezentują TO SAMO zamówienie (ta sama tożsamość Id), nawet jeśli stan się różni

Entity to obiekt, którego tożsamość (zwykle Id) definiuje go bardziej niż aktualne wartości jego pól — zamówienie #5 pozostaje “tym samym” zamówieniem, niezależnie od tego, ile razy zmieni się jego status.

Value Object — wartość ważniejsza niż tożsamość

public record Adres(string Ulica, string Miasto, string KodPocztowy); // Value Object — bez Id

var a1 = new Adres("Długa 5", "Warszawa", "00-001");
var a2 = new Adres("Długa 5", "Warszawa", "00-001");
Console.WriteLine(a1 == a2); // True — te same wartości = ten sam obiekt logicznie

Odwrotnie niż Entity: Adres nie ma tożsamości — dwa adresy o tych samych wartościach są równoważne, kropka. Value Objects zasługują na osobny, dogłębny artykuł — sprawdź Value Object w DDD — implementacja w C# z rekordem, gdzie rozkładamy je znacznie szerzej.

Aggregate — granica spójności

public class Zamowienie // Aggregate Root — jedyny punkt wejścia do modyfikacji całego agregatu
{
    private readonly List<PozycjaZamowienia> _pozycje = new();
    public IReadOnlyList<PozycjaZamowienia> Pozycje => _pozycje;

    public void DodajPozycje(Produkt produkt, int ilosc)
    {
        if (ilosc <= 0) throw new ArgumentException("Ilość musi być dodatnia");
        _pozycje.Add(new PozycjaZamowienia(produkt, ilosc));
    }
}

Zamowienie to Aggregate Root — kod z zewnątrz nigdy nie modyfikuje _pozycje bezpośrednio, tylko przez metody Zamowienie (DodajPozycje), które pilnują reguł spójności (np. dodatnia ilość). Agregat to granica, w obrębie której wszystkie reguły biznesowe są zawsze spełnione.

Repository — dostęp do agregatów bez ujawniania szczegółów bazy

public interface IZamowieniaRepository
{
    Zamowienie? ZnajdzPoId(int id);
    void Zapisz(Zamowienie zamowienie);
}

Repository udostępnia agregaty tak, jakby żyły w pamięci — kod domenowy nie wie, czy ZnajdzPoId odpytuje SQL Server, czy plik JSON. To ten sam mechanizm, który napędza regułę zależności w Clean Architecture — DDD i Clean Architecture naturalnie się uzupełniają.

Kiedy DDD to za dużo

Pełne DDD (bounded context, agregaty, event storming) ma sens dla złożonych domen biznesowych z realnymi regułami do wymodelowania. Dla prostego CRUD-a (lista zadań, blog) to narzut bez korzyści — KISS i YAGNI dotyczą też wyboru, ile DDD faktycznie potrzebujesz.

Podsumowanie

Pięć building blocków DDD, które zobaczysz w niemal każdym poważniejszym projekcie .NET: Ubiquitous Language (nazwy z języka biznesu), Entity (tożsamość ważniejsza niż wartości), Value Object (odwrotnie — wartość, nie tożsamość), Aggregate (granica spójności z jednym punktem wejścia) i Repository (dostęp do agregatów bez ujawniania bazy danych). Każdy z nich rozwiązuje inny, konkretny problem modelowania — DDD to nie jeden wzorzec, tylko zestaw narzędzi do wyboru zależnie od złożoności domeny, którą modelujesz.

👨‍💻
Mariusz Jurczenko
Senior .NET Developer · 10+ lat doświadczenia komercyjnego

Programista .NET z doświadczeniem komercyjnym w firmach takich jak NFZ, Kamsoft, Diagnostyka, Hermes Reply Polska czy Etisoft Smart Solutions. Twórca kursów, z których skorzystało już ponad 11 000 osób w Strefie Kursów i ponad 1 000 kursantów na dev-hobby.pl.

Specjalizacja: Clean Code, Clean Architecture i uczenie programowania tak, żeby dało się je naprawdę zrozumieć — nie wykuć.

🚀 Co dalej?

Zobacz to w praktyce na wideo i pobierz darmową roadmapę, żeby ułożyć naukę w spójną ścieżkę do pierwszej pracy.

1 comment

Dodaj komentarz

czytanie to początek

Zamień wiedzę w umiejętności

Pobierz darmową Roadmapę .NET i ułóż takie tematy jak ten w spójną ścieżkę do pierwszej pracy.

Pobieram roadmapę →