Dwa tygodnie po pierwszym commicie produktu. Siedzę z kodem, który działa — i widzę, że przepisuję te same struktury po raz trzeci. Agregat tu, encja tam, zdarzenie domenowe gdzie indziej. Za każdym razem od zera.
Wtedy pojawia się pytanie, które pewnie każdy solo developer zadaje sobie raz za razem: napisać to od nowa, kupić gotowe, czy może… wyciągnąć własne?
Trzy opcje, jedna głowa
Kiedy budujesz coś poważnego — platformę z logiką domeny, regułami biznesowymi, zdarzeniami — prędzej czy później potrzebujesz jakiegoś fundamentu pod DDDDDD (Domain-Driven Design)Projektowanie kodu wokół biznesu, nie technologii — kod odzwierciedla to, co aplikacja robi dla użytkownika. czytaj więcej → . Pytanie brzmi: skąd go wziąć?
Buy — kup gotowy framework. W ekosystemie .NET są NServiceBus, MassTransit, Ardalis Clean Architecture. W Node.js — ubóstwo. Są biblioteki do zdarzeń, są do walidacji, ale nic co spinałoby to w spójny zestaw wzorców, z testowalnością i TypeScriptem. Praktycznie: opcja buy odpada.
Build — napisz od zera w produkcie. Najszybszy start. Ale: dwa projekty później masz dwa zestawy tych samych wzorców, które żyją osobno, ewoluują osobno, psują się osobno.
Wydziel-własne — zrób bibliotekę raz, użyj wszędzie. Koszt: trzeba ją faktycznie utrzymywać.
Wybrałem trzecią opcję. Repozytorium vytches-ddd założyłem pod koniec czerwca,
a w połowie lipca wydałem pierwszą wersję. Prawie dwa miesiące przerwy od
produktu — o czym opiszę osobno w
„Świadomej pauzie”.
Co trafiło do biblioteki
vytches-ddd to monorepo oparte na NX i pnpm. Na start wylądowało w nim
ponad 20 pakietów: agregaty (aggregates), obiekty wartości (value-objects),
zdarzenia domenowe (events), wzorzec rezultatu (result-pattern), repozytoria,
polityki, serwisy domenowe, warstwa CQRS, testowanie, walidacja i kilka innych.
Każdy pakiet jest oddzielną jednostką z własnym package.json, publikowaną
przez CI do GitHub Packages (prywatny rejestr). W produkcie — jeden wpis
w .npmrc i normalne npm install.
Czy to over-engineering?
Uczciwa odpowiedź: może. Ale over-engineering to rozwiązywanie problemów, których nie masz. Ja miałem ten problem — przepisywałem te same wzorce po raz drugi i wiedziałem, że to nie ostatni projekt.
Kluczowe pytanie nie brzmi „czy biblioteka to dobry pomysł ogólnie”. Brzmi: „czy będę faktycznie utrzymywał drugi projekt?”. Jeśli odpowiedź jest „nie wiem”, wydzielanie własnej biblioteki to przedwczesna inwestycja.
U mnie odpowiedź była „tak” — mam plan na kilka produktów na tym samym stosie. Biblioteka to fundament systemu, nie jednorazowy kaprys.
Alternatywa, której nie wybrałem: podfolder shared/ wewnątrz monorepo
produktu. Prostszy start, gorszy podział. Można do niego wrócić, jeśli plan
się zmieni.
Co oznacza „ponad 20 pakietów” w praktyce
Nie wrzuciłem ich wszystkich naraz i nie ogłosiłem sukcesu. Pierwsza wersja
v0.1.0 miała to, czego produkt potrzebował na dzień pierwszy:
agregaty, obiekty wartości, zdarzenia, repozytoria, wynik domenowy
(Result patternWzorzec ResultWynik operacji jako wartość (sukces albo błąd) zamiast rzucania wyjątku — logika domeny zostaje czysta.
czytaj więcej →
). Reszta powstawała
w kolejnych tygodniach, gdy produkt sygnalizował potrzebę.
Biblioteka żyje w rytmie produktu — nie odwrotnie.
Dalej
W sierpniu 2025, gdy produkt ruszył na pełnych obrotach, biblioteka stanęła w tle — ale była już tam i działała. Parę tygodni później podjąłem decyzję, żeby ponownie ją zatrzymać i mocno zrefaktoryzować. Ale to już osobna historia.
Jak to działa od środka (dla ciekawych)
Struktura monorepo
vytches-ddd to NX monorepo z pnpm workspaces. Każdy pakiet pod
packages/<nazwa>/, każdy z osobnym package.json i własnym cyklem wydań.
Przykładowe pakiety: @vytches/ddd-core, @vytches/ddd-cqrs,
@vytches/ddd-events, @vytches/ddd-policies, @vytches/ddd-testing,
@vytches/ddd-utils.
Podłączenie do produktu
# .npmrc w projekcie produktowym
@vytches:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}Pakiety zainstalowane jak każda zależność npm:
{
"dependencies": {
"@vytches/ddd": "^0.4.0"
}
}W lipcu w produkcie pojawił się tylko wpis w .npmrc (rejestr) — faktyczna
zależność @vytches/ddd weszła do package.json na początku sierpnia, już
w wersji ^0.4.0. Biblioteka przez tych kilka tygodni zdążyła dojrzeć szybciej
niż produkt, który miał z niej korzystać.
Kiedy NIE robić własnej biblioteki
- Masz jeden projekt i jeden stos — zwykły
shared/w monorepo wystarczy. - Nie masz planu na drugi projekt — to inwestycja bez zwrotu.
- Nie lubisz zarządzać CI dla N pakietów — próg wejścia jest realny.