Enterprise Patterns
Language-agnostic tutorials based on "Enterprise Patterns and MDA" by Jim Arlow & Ila Neustadt
Software Archetypes is an open reference for the data patterns that underpin almost every business system: how to model people and organizations, products and catalogs, orders, payments, inventory and shipments. Each pattern starts from a real problem, walks through the structure with a UML class diagram, then shows the SQL schema, the lifecycle as a state machine, and the invariants that must hold at runtime.
The patterns are based on Jim Arlow and Ila Neustadt's Enterprise Patterns and MDA, refined with two decades of building production systems. They're written for backend engineers and architects who want a starting point that's been battle-tested across industries — not a textbook abstraction, but a working data model you can lift into PostgreSQL, MySQL or any RDBMS today.
All examples ship with working implementations in PHP, Java and TypeScript. Each pattern is roughly 10–15 minutes of reading and a self-contained mental model you can apply on its own or compose with the others.
Overview
Each pattern is presented with:
- Problem statement — When you need this pattern
- Conceptual overview — Core idea
- Structure — Mermaid class diagrams
- Behaviors — Pseudocode for operations
- State machines — Lifecycle diagrams
- Invariants — Business rules that must hold
- SQL schema — ANSI SQL for persistence
- Design considerations — Trade-offs and alternatives
Pattern Catalog
Party Archetype (Who)
| Pattern | File | Description |
|---|---|---|
| Party | 01-party-pattern.md | Person or Organization abstraction |
| Party Role | 02-party-role-pattern.md | Customer, Supplier, Employee roles |
| Party Relationship | 03-party-relationship-pattern.md | Employment, Ownership, Marriage |
| Party Identifier | 04-party-identifier-pattern.md | PESEL, NIP, Passport, Tax ID |
Product Archetype (What)
| Pattern | File | Description |
|---|---|---|
| Product | 05-product-archetype.md | ProductType → Product → ProductInstance |
Transaction Patterns (Commerce)
| Pattern | File | Description |
|---|---|---|
| Order | 06-order-archetype.md | Order + OrderLine, lifecycle |
| Payment | 07-payment-pattern.md | Payments and Refunds |
| Inventory | 08-inventory-pattern.md | Stock tracking, transactions |
| Shipment | 09-shipment-pattern.md | Physical delivery, tracking |
Pattern Relationships
End-to-End Integration Scenario
This walkthrough shows how all patterns connect in a typical e-commerce flow: a customer places an order, pays, stock is reserved, and the order is shipped.
1. Party Setup
// 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. Order Placement
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. Inventory Reservation
// 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. Shipment Creation & Warehouse Flow
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. Inventory Issue
// 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. Delivery
// Carrier webhook updates
shipment.markInTransit("Warszawa Hub")
shipment.markOutForDelivery()
shipment.markDelivered({ signature: "J. Kowalski" })
// emits ShipmentDelivered
order.markDelivered()
order.complete()
// emits OrderCompletedEvent Flow Summary
Common Design Principles
IDs and Keys
- Primary key: UUID v7 (time-sortable)
- Business key: Human-readable (ORD-2024-000001)
- External key: From other systems (gateway transaction ID)
Money
// NEVER use floating point!
// Store in smallest currency unit (cents, grosze)
amount_cents: 19999 // = 199.99
currency: "PLN"Time
- Timestamps: UTC, immutable (createdAt)
- Dates: Local date without time (birthDate)
- Validity period: validFrom/validTo for time-bounded entities
Inheritance Strategies
| Strategy | Use Case | SQL |
|---|---|---|
| Single Table | Few subtype fields | One table, type column |
| Joined | Many subtype-specific fields | Base + subtype tables |
| Concrete | Completely different subtypes | Separate tables |
Domain Events Catalog
Every pattern emits domain events when important state changes occur. These events enable loose coupling between patterns (e.g., OrderConfirmed triggers inventory reservation).
Party Domain
| Event | Source | Trigger | Payload |
|---|---|---|---|
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 |
Product Domain
| Event | Source | Trigger | Payload |
|---|---|---|---|
ProductDiscontinued |
Product | Product.discontinue() |
product |
InstanceSold |
Product | ProductInstance.sell() |
instance, order |
Commerce Domain
| Event | Source | Trigger | Payload |
|---|---|---|---|
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 |
Fulfillment Domain
| Event | Source | Trigger | Payload |
|---|---|---|---|
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 |
Key Event Chains
| Trigger Event | Typical Listener Action |
|---|---|
OrderConfirmed |
Reserve inventory for each order line |
OrderCancelled |
Release inventory reservations |
PaymentCompleted |
Mark order as paid, create shipment |
ShipmentShipped |
Issue reserved stock from inventory |
ShipmentDelivered |
Mark order as delivered |
ShipmentReturned |
Create return record, restock inventory |
Rendering Mermaid Diagrams
These tutorials use Mermaid for diagrams. To render them:
- GitHub — Renders automatically in Markdown files
- VS Code — Install "Markdown Preview Mermaid Support" extension
- Online — Use mermaid.live
- CLI — Use
mmdc(mermaid-cli)
Working Implementations
Three complete implementations available in a separate repository: software-archetypes-examples
| Stack | Directory | Run |
|---|---|---|
| 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 |
Each demo creates the same scenario: Jan Kowalski (Person) places an order at Acme (Organization), pays by card, stock is reserved and shipped via DHL, and delivered.
Glossary
| Term | Definition |
|---|---|
| Archetype | A recurring, universal pattern in business modeling. Archetypes (Party, Product, Order) appear across virtually every enterprise system regardless of industry. |
| Party | An abstraction for any entity — person or organization — that can participate in transactions, hold roles, or enter relationships. |
| Person | A concrete Party subtype representing an individual human being. |
| Organization | A concrete Party subtype representing a company, institution, or group. |
| Party Role | A function or capacity in which a Party acts within the system (Customer, Supplier, Employee). Separates identity from capability. |
| Party Relationship | A directed connection between two Parties with its own attributes and lifecycle (Employment, Ownership, Marriage). |
| Party Identifier | An external identity code assigned to a Party by an issuing authority (PESEL, NIP, Passport). |
| ProductType | A category or template that defines the structure (attributes, rules) for a group of products. Forms a hierarchy (Electronics > Laptops). |
| Product | A catalog item with a SKU and price that can be ordered. Belongs to a ProductType. |
| ProductInstance | A specific physical or serialized unit of a Product, tracked individually (by serial number, batch, expiry). |
| Order | A commercial transaction recording what a Customer wants to buy, with header info and line items. |
| OrderLine | A single item within an Order: product, quantity, unit price, and calculated totals. |
| Payment | A financial transaction representing money moving in (payment) or out (refund). Has its own lifecycle independent of Order. |
| Refund | A Payment with isRefund = true, linked to the original payment. Models money returning to the customer. |
| InventoryItem | Stock of a specific Product at a specific Location. Tracks on-hand, reserved, and available quantities. |
| InventoryTransaction | An immutable ledger entry recording a stock change (receipt, issue, reserve, release, adjustment, transfer). |
| Location | A physical or virtual place where inventory is stored, forming a hierarchy (Warehouse > Zone > Aisle > Bin). |
| Shipment | The physical movement of goods from origin to destination. Has its own lifecycle separate from Order. |
| ShipmentEvent | A timestamped status change in a Shipment's journey (picked up, in transit, delivered). |
| Carrier | A shipping provider (DHL, UPS, InPost) with tracking integration. |
| Money | A value object representing a monetary amount stored as an integer in the smallest currency unit (cents/grosze) plus a currency code. Never use floating point. |
| Domain Event | A notification emitted when something significant happens (OrderConfirmed, PaymentCompleted). Used for loose coupling between patterns. |
| Invariant | A business rule that must always be true (e.g., "reserved cannot exceed on-hand"). Enforced by preconditions and database constraints. |
| Precondition | A condition checked before an operation executes (require). Throws an error if violated. |
| EAV | Entity-Attribute-Value — a schema pattern for dynamic attributes where each attribute is stored as a row rather than a column. |
| SKU | Stock Keeping Unit — a unique code identifying a product in the catalog. |
| UUID v7 | A time-sortable universally unique identifier. Recommended as primary key for all entities. |
License
MIT License. Pattern concepts based on "Enterprise Patterns and MDA" by Jim Arlow & Ila Neustadt (Addison-Wesley, 2003).