Software Archetypes
Na podstawie "Enterprise Patterns and MDA" Jima Arlowa i Ili Neustadt
Software Archetypes to otwarte źródło wiedzy o wzorcach danych, które stoją za prawie każdym systemem biznesowym: jak modelować osoby i organizacje, produkty i katalogi, zamówienia, płatności, magazyn i wysyłki. Każdy wzorzec zaczyna się od konkretnego problemu, prowadzi przez strukturę z diagramem klas UML, a następnie pokazuje schemat SQL, cykl życia jako maszynę stanów i niezmienniki, które muszą zachodzić w czasie działania.
Wzorce bazują na książce Enterprise Patterns and MDA Jima Arlowa i Ili Neustadt, zweryfikowanej dwiema dekadami budowania systemów produkcyjnych. Są napisane dla inżynierów backendowych i architektów, którzy potrzebują punktu wyjścia sprawdzonego w wielu branżach — nie podręcznikowej abstrakcji, ale działającego modelu danych, który można od razu wdrożyć w PostgreSQL, MySQL lub dowolnej bazie relacyjnej.
Wszystkie przykłady mają działające implementacje w PHP, Javie i TypeScript. Każdy wzorzec to około 10–15 minut czytania i samodzielnie zrozumiały model, który można zastosować osobno lub złożyć z innymi.
Przegląd
Każdy wzorzec jest przedstawiony wraz z:
- Opis problemu — Kiedy potrzebujesz tego wzorca
- Przegląd koncepcyjny — Główna idea
- Struktura — Diagramy klas Mermaid
- Zachowania — Pseudokod operacji
- Maszyny stanów — Diagramy cyklu życia
- Niezmienniki — Reguły biznesowe, które muszą być spełnione
- Schemat SQL — ANSI SQL do persystencji
- Zagadnienia projektowe — Kompromisy i alternatywy
Katalog wzorców
Archetyp Party (Kto)
| Wzorzec | Plik | Opis |
|---|---|---|
| Party | 01-party-pattern.md | Abstrakcja Person lub Organization |
| Party Role | 02-party-role-pattern.md | Role: Customer, Supplier, Employee |
| Party Relationship | 03-party-relationship-pattern.md | Employment, Ownership, Marriage |
| Party Identifier | 04-party-identifier-pattern.md | PESEL, NIP, Paszport, Numer podatkowy |
Archetyp Product (Co)
| Wzorzec | Plik | Opis |
|---|---|---|
| Product | 05-product-archetype.md | ProductType → Product → ProductInstance |
Wzorce transakcyjne (Handel)
| Wzorzec | Plik | Opis |
|---|---|---|
| Order | 06-order-archetype.md | Order + Order Line, cykl życia |
| Payment | 07-payment-pattern.md | Payment i Refund |
| Inventory | 08-inventory-pattern.md | Śledzenie stanów magazynowych, transakcje |
| Shipment | 09-shipment-pattern.md | Dostawa fizyczna, śledzenie |
Relacje między wzorcami
Scenariusz integracji end-to-end
Ten przewodnik pokazuje, jak wszystkie wzorce łączą się w typowym przepływie e-commerce: Customer składa Order, płaci, towar jest rezerwowany, a Order jest wysyłane.
1. Konfiguracja Party
// Jan Kowalski is registered as a Person with a Customer role
jan = new Person(firstName: "Jan", lastName: "Kowalski")
jan.addIdentifier(PESEL, "85031512345")
jan.addAddress(homeAddress)
customerRole = new Customer(
party: jan,
customerNumber: "CUST-00001234",
loyaltyTier: LoyaltyTier.Gold,
creditLimit: Money(1000000, "PLN")
)2. Złożenie Order
order = new Order(
orderNumber: "ORD-2026-000042",
customer: customerRole,
currency: "PLN",
billingAddress: jan.getPrimaryAddress()
)
order.addLine(thinkpad, quantity: 1, unitPrice: Money(649900, "PLN"))
order.addLine(mouse, quantity: 2, unitPrice: Money(44900, "PLN"))
// order.grandTotal = 909633 (with 23% VAT)
order.confirm()
// emits OrderConfirmed → triggers inventory reservation3. Rezerwacja Inventory
// Listener handles OrderConfirmed event
for line in order.lines:
item = findInventoryItem(line.product, warehouse)
item.reserve(line.quantity, order.orderNumber, "Order reservation")
// emits StockReserved
// Warehouse state:
// ThinkPad: onHand=20, reserved=1, available=19
// Mouse: onHand=50, reserved=2, available=484. Payment
payment = new Payment(
reference: "PAY-2026-000089",
order: order,
amount: order.grandTotal,
method: PaymentMethod.Card
)
payment.markProcessing()
// ... gateway webhook: payment.success ...
payment.complete("gw_txn_abc123")
// emits PaymentCompleted → triggers shipment creation
order.markPaid(payment)
// emits OrderPaid5. Utworzenie Shipment i przepływ Inventory
shipment = Shipment.fromOrder(order, "SHP-2026-000018", warehouseAddress)
shipment.addItemFromOrderLine(order.lines[0], 1) // ThinkPad
shipment.addItemFromOrderLine(order.lines[1], 2) // Mouse
shipment.startPicking() // warehouse staff collects items
shipment.markPacked() // items boxed
shipment.assignCarrier(dhl, "1234567890")
shipment.markLabelPrinted()
shipment.handOverToCarrier()
// emits ShipmentShipped6. Wydanie Inventory
// Listener handles ShipmentShipped event
for item in shipment.items:
inv = findInventoryItem(item.product, warehouse)
inv.shipReserved(item.quantity, order.orderNumber)
// emits StockIssued
// Warehouse state:
// ThinkPad: onHand=19, reserved=0, available=19
// Mouse: onHand=48, reserved=0, available=487. Dostawa
// Carrier webhook updates
shipment.markInTransit("Warszawa Hub")
shipment.markOutForDelivery()
shipment.markDelivered({ signature: "J. Kowalski" })
// emits ShipmentDelivered
order.markDelivered()
order.complete()
// emits OrderCompletedPodsumowanie przepływu zdarzeń
Wspólne zasady projektowe
Identyfikatory i klucze
- Klucz główny: UUID v7 (sortowalny chronologicznie)
- Klucz biznesowy: Czytelny dla człowieka (ORD-2024-000001)
- Klucz zewnętrzny: Z innych systemów (identyfikator transakcji bramki płatniczej)
Pieniądze
// NEVER use floating point!
// Store in smallest currency unit (cents, grosze)
amount_cents: 19999 // = 199.99
currency: "PLN"Czas
- Znaczniki czasu: UTC, niezmienne (createdAt)
- Daty: Data lokalna bez czasu (birthDate)
- Okres ważności: validFrom/validTo dla encji ograniczonych czasowo
Strategie dziedziczenia
| Strategia | Zastosowanie | SQL |
|---|---|---|
| Jedna tabela | Niewiele pól podtypów | Jedna tabela, kolumna typu |
| Łączenie tabel | Wiele pól specyficznych dla podtypów | Tabela bazowa + tabele podtypów |
| Tabela konkretna | Zupełnie różne podtypy | Osobne tabele |
Katalog zdarzeń domenowych
Każdy wzorzec emituje zdarzenia domenowe, gdy zachodzą istotne zmiany stanu. Zdarzenia te umożliwiają luźne powiązanie między wzorcami (np. OrderConfirmed wyzwala rezerwację magazynową).
Domena Party
| Zdarzenie | Źródło | Wyzwalacz | Dane |
|---|---|---|---|
CustomerTierUpgraded |
Party Role | Customer.upgradeTier() |
customer, previousTier, newTier |
EmployeePromoted |
Party Role | Employee.promote() |
employee, previousPosition, newPosition |
RelationshipTerminated |
Party Relationship | PartyRelationship.terminate() |
relationship, endDate, reason |
EmployeePromoted |
Party Relationship | Employment.promote() |
employment, previousTitle, newTitle |
EmployeeTransferred |
Party Relationship | Employment.transfer() |
employment, newDepartment, newLocation |
OwnershipChanged |
Party Relationship | Ownership.adjustOwnership() |
ownership, previousPct, newPct |
IdentifierVerified |
Party Identifier | PartyIdentifier.verify() |
identifier, verifier |
IdentifierRevoked |
Party Identifier | PartyIdentifier.revoke() |
identifier, reason |
Domena Product
| Zdarzenie | Źródło | Wyzwalacz | Dane |
|---|---|---|---|
ProductDiscontinued |
Product | Product.discontinue() |
product |
InstanceSold |
Product | ProductInstance.sell() |
instance, order |
Domena Handlowa
| Zdarzenie | Źródło | Wyzwalacz | Dane |
|---|---|---|---|
OrderConfirmed |
Order | Order.confirm() |
order |
OrderPaid |
Order | Order.markPaid() |
order, payment |
OrderCancelled |
Order | Order.cancel() |
order, reason |
PaymentProcessing |
Payment | Payment.markProcessing() |
payment |
PaymentCompleted |
Payment | Payment.complete() |
payment |
PaymentFailed |
Payment | Payment.fail() |
payment, reason |
PaymentCancelled |
Payment | Payment.cancel() |
payment |
Domena Realizacji
| Zdarzenie | Źródło | Wyzwalacz | Dane |
|---|---|---|---|
StockReceived |
Inventory | InventoryItem.receive() |
item, quantity, reference |
StockIssued |
Inventory | InventoryItem.issue() |
item, quantity, reference |
StockReserved |
Inventory | InventoryItem.reserve() |
item, quantity, reference |
StockReleased |
Inventory | InventoryItem.release() |
item, quantity, reference |
StockAdjusted |
Inventory | InventoryItem.adjust() |
item, quantity, reference |
StockTransferred |
Inventory | InventoryService.transfer() |
source, destination, quantity, reference |
ShipmentShipped |
Shipment | Shipment.handOverToCarrier() |
shipment |
ShipmentDelivered |
Shipment | Shipment.markDelivered() |
shipment |
ShipmentReturned |
Shipment | Shipment.markReturned() |
shipment |
ShipmentCancelled |
Shipment | Shipment.cancel() |
shipment |
Kluczowe łańcuchy zdarzeń
| Zdarzenie wyzwalające | Typowa akcja nasłuchiwacza |
|---|---|
OrderConfirmed |
Rezerwacja magazynowa dla każdej Order Line |
OrderCancelled |
Zwolnienie rezerwacji magazynowych |
PaymentCompleted |
Oznaczenie Order jako opłacone, utworzenie Shipment |
ShipmentShipped |
Wydanie zarezerwowanego towaru z Inventory |
ShipmentDelivered |
Oznaczenie Order jako dostarczone |
ShipmentReturned |
Utworzenie rekordu Refund, uzupełnienie Inventory |
Renderowanie diagramów Mermaid
Te samouczki używają Mermaid do diagramów. Aby je wyświetlić:
- GitHub — Renderuje automatycznie w plikach Markdown
- VS Code — Zainstaluj rozszerzenie "Markdown Preview Mermaid Support"
- Online — Użyj mermaid.live
- CLI — Użyj
mmdc(mermaid-cli)
Działające implementacje
Trzy kompletne implementacje dostępne w osobnym repozytorium: software-archetypes-examples
| Stos technologiczny | Katalog | Uruchomienie |
|---|---|---|
| PHP 8.3 / Symfony 7 / Doctrine ORM 3 | php/ |
cd php && make up && make demo |
| Java 21 / Spring Boot 3.4 / JPA+Hibernate | java/ |
cd java && make up && make demo |
| TypeScript / Node 22 / TypeORM 0.3 | nodejs/ |
cd nodejs && make build && make demo |
Każde demo tworzy ten sam scenariusz: Jan Kowalski (Person) składa Order w Acme (Organization), płaci kartą, towar jest rezerwowany i wysyłany przez DHL, a następnie dostarczany.
Słowniczek
| Termin | Definicja |
|---|---|
| Archetyp | Powtarzalny, uniwersalny wzorzec w modelowaniu biznesowym. Archetypy (Party, Product, Order) pojawiają się praktycznie w każdym systemie korporacyjnym, niezależnie od branży. |
| Party | Abstrakcja dla dowolnej encji — Person lub Organization — która może uczestniczyć w transakcjach, pełnić role lub wchodzić w relacje. |
| Person | Konkretny podtyp Party reprezentujący pojedynczego człowieka. |
| Organization | Konkretny podtyp Party reprezentujący firmę, instytucję lub grupę. |
| Party Role | Funkcja lub zdolność, w jakiej Party działa w systemie (Customer, Supplier, Employee). Oddziela tożsamość od możliwości. |
| Party Relationship | Skierowane powiązanie między dwoma Party z własnymi atrybutami i cyklem życia (Employment, Ownership, Marriage). |
| Party Identifier | Zewnętrzny kod identyfikacyjny przypisany Party przez organ wydający (PESEL, NIP, Paszport). |
| ProductType | Kategoria lub szablon definiujący strukturę (atrybuty, reguły) dla grupy produktów. Tworzy hierarchię (Elektronika > Laptopy). |
| Product | Pozycja katalogowa z SKU i ceną, którą można zamówić. Należy do ProductType. |
| ProductInstance | Konkretna fizyczna lub serializowana jednostka Product, śledzona indywidualnie (po numerze seryjnym, partii, dacie ważności). |
| Order | Transakcja handlowa rejestrująca, co Customer chce kupić, z danymi nagłówkowymi i pozycjami. |
| Order Line | Pojedyncza pozycja w Order: produkt, ilość, cena jednostkowa i obliczone sumy. |
| Payment | Transakcja finansowa reprezentująca wpływ pieniędzy (Payment) lub ich wypływ (Refund). Ma własny cykl życia niezależny od Order. |
| Refund | Payment z isRefund = true, powiązana z oryginalną Payment. Modeluje zwrot pieniędzy do Customer. |
| Inventory Item | Stan magazynowy konkretnego Product w konkretnej Location. Śledzi ilości: dostępne, zarezerwowane i na stanie. |
| Inventory Transaction | Niezmienny wpis w księdze rejestrujący zmianę stanu magazynowego (przyjęcie, wydanie, rezerwacja, zwolnienie, korekta, transfer). |
| Location | Fizyczne lub wirtualne miejsce, w którym przechowywany jest towar, tworzące hierarchię (Warehouse > Strefa > Alejka > Regał). |
| Shipment | Fizyczne przemieszczenie towaru z miejsca nadania do miejsca docelowego. Ma własny cykl życia oddzielony od Order. |
| Shipment Event | Oznaczona czasowo zmiana statusu w podróży Shipment (odebrana, w tranzycie, dostarczona). |
| Carrier | Dostawca usług wysyłkowych (DHL, UPS, InPost) z integracją śledzenia przesyłek. |
| Pieniądze (Money) | Obiekt wartości reprezentujący kwotę pieniężną przechowywaną jako liczba całkowita w najmniejszej jednostce walutowej (centy/grosze) plus kod waluty. Nigdy nie używaj zmiennoprzecinkowych. |
| Zdarzenie domenowe (Domain Event) | Powiadomienie emitowane, gdy dzieje się coś istotnego (OrderConfirmed, PaymentCompleted). Używane do luźnego powiązania między wzorcami. |
| Niezmiennik (Invariant) | Reguła biznesowa, która musi być zawsze prawdziwa (np. „zarezerwowane nie może przekraczać stanu na ręku"). Wymuszana przez warunki wstępne i ograniczenia bazodanowe. |
| Warunek wstępny (Precondition) | Warunek sprawdzany przed wykonaniem operacji (require). Rzuca błąd, jeśli jest naruszony. |
| EAV | Entity-Attribute-Value — wzorzec schematu dla dynamicznych atrybutów, w którym każdy atrybut jest przechowywany jako wiersz, a nie kolumna. |
| SKU | Stock Keeping Unit — unikalny kod identyfikujący produkt w katalogu. |
| UUID v7 | Sortowalny chronologicznie uniwersalny unikalny identyfikator. Zalecany jako klucz główny dla wszystkich encji. |
Licencja
Licencja MIT. Koncepcje wzorców na podstawie "Enterprise Patterns and MDA" autorstwa Jima Arlowa i Ili Neustadt (Addison-Wesley, 2003).