Designing the System
Identifying Domain Entities
আপনি একটি free preview lesson দেখছেন।
আগের lesson-এ আমরা requirements থেকে technical design-এর দিকে যাওয়া শুরু করেছি।
আমরা established করেছি যে system design শুরু করা উচিত business behaviour থেকে:
Business Behaviour
↓
Responsibilities
↓
Domain Concepts
↓
Technical Design
এখন আমাদের Order Management Backend-এর গুরুত্বপূর্ণ domain concepts identify করতে হবে।
Requirements-এ আমরা বারবার দেখেছি:
Customer
Product
Inventory
Order
Order Item
Payment
কিন্তু noun দেখেই একটি Java class তৈরি করা domain modeling নয়।
আমাদের বুঝতে হবে:
- কোন concept-এর identity গুরুত্বপূর্ণ
- কোন concept-এর lifecycle আছে
- কোন concept অন্য concept-এর অংশ
- কোন data current state এবং কোন data historical fact
- কোন business rule কোথায় protect করা উচিত
- কোন concept independent entity এবং কোনটি অন্য entity-এর ownership-এর মধ্যে থাকবে
এই lesson-এ আমরা implementation বা JPA mapping করব না।
Goal হলো business domain-এর একটি পরিষ্কার model তৈরি করা।
What Is a Domain?
Software engineering-এ domain হলো সেই problem space যার জন্য আমরা software তৈরি করছি।
আমাদের broad domain commerce হলেও আমরা পুরো commerce platform তৈরি করছি না।
Current problem space:
Product catalog
Inventory
Ordering
Order history
Cancellation
Payment
আমাদের current scope-এর বাইরে:
Shipping
Warehouses
Discounts
Coupons
Returns
Refunds
Recommendations
Supplier management
Accounting
এই boundary গুরুত্বপূর্ণ।
বাস্তব commerce platform-এ কোনো concept থাকতে পারে বলেই আমাদের current application-এ সেটি model করার প্রয়োজন নেই।
Domain model should represent the problem we are solving now.
What Is a Domain Model?
Domain model business concepts, relationships, state এবং behaviour represent করে।
For example, Order শুধু data নয়:
id
customerId
status
total
Order-এর behaviour-ও আছে:
Can it be paid?
Can it be cancelled?
Which customer owns it?
Which items belong to it?
Which state transitions are valid?
তাই useful domain model-এর মধ্যে থাকে:
Identity
+
State
+
Relationships
+
Business Rules
+
Behaviour
What Is an Entity?
একটি Entity এমন domain concept যার identity গুরুত্বপূর্ণ।
ধরা যাক:
Product #101
Name: Keyboard
Price: €50
এবং:
Product #102
Name: Keyboard
Price: €50
Properties একই হলেও:
Product #101 != Product #102
কারণ identity আলাদা।
Entity-এর properties সময়ের সঙ্গে পরিবর্তিত হতে পারে, কিন্তু identity একই থাকে।
For example:
Product #101
€50
↓
€55
এটি এখনও Product #101।
Entity Is Not the Same as JPA @Entity
Java/Spring development-এ @Entity persistence-এর একটি concept।
Domain modeling-এ Entity business identity এবং lifecycle-এর concept।
যখন আমরা বলি:
Order is a domain entity.
তার অর্থ এই নয় যে আমরা ইতোমধ্যে decide করেছি exact same Java class-এ:
@Entity
ব্যবহার করব।
Persistence model পরে design করা হবে।
Similarly:
Domain Entity
≠
Database Table
যদিও অনেক domain entity শেষ পর্যন্ত table-এ persist হবে।
Value-Like Concepts
সব concept-এর independent identity প্রয়োজন হয় না।
For example:
€20
আরেকটি:
€20
একই monetary context-এ value-wise equivalent।
আমাদের Money #123 এবং Money #456 identity প্রয়োজন নেই।
এ ধরনের concept value দিয়ে meaningful।
Examples:
Money
Quantity
Order Status
তবে প্রতিটি value-এর জন্য custom Java class বানাতেই হবে এমন নয়।
Domain concept এবং implementation abstraction আলাদা decision।
Start From Business Language
আমাদের requirements বলছে:
Customers place orders.
Orders contain products.
Products have inventory.
Orders preserve purchase-time prices.
Customers can cancel eligible orders.
Customers can pay for orders.
এগুলো আমাদের domain concepts identify করতে সাহায্য করে।
এখন প্রতিটি concept analyse করি।
Customer
Customer:
- order create করে
- নিজের orders দেখে
- নিজের eligible order cancel করে
- নিজের order pay করে
Customer identity গুরুত্বপূর্ণ কারণ:
Customer A
এবং:
Customer B
এক নয়।
Critical ownership rule:
Customer A cannot access Customer B's orders.
তাই ordering domain-এর জন্য একটি stable customer identity প্রয়োজন।
Do We Need a Full Customer Entity?
আমাদের engineering context অনুযায়ী authentication একটি existing identity capability provide করে।
আমাদের system build করছে না:
Registration
Password storage
Password reset
Email verification
MFA
তাই Order Management Backend-এর জন্য full identity/account model প্রয়োজন নেই।
Current ordering domain মূলত need করে:
CustomerId
যাতে বলা যায়:
This order belongs to Customer X.
External identity system own করবে user authentication।
Our backend own করবে order ownership।
Conceptually:
Identity System
↓
authenticated user identity
↓
Order Management Backend
এটি important boundary।
Product
Product clearly একটি domain entity।
Product-এর:
- stable identity আছে
- current catalog data আছে
- current price আছে
- active/inactive ordering state আছে
- lifecycle আছে
Example:
Product #101
Price: €20
Status: ACTIVE
Later:
Product #101
Price: €25
Status: ACTIVE
Still same Product।
Later:
Product #101
Status: INACTIVE
Still same Product।
Therefore:
Product is a domain entity.
Product Responsibilities
Current requirements অনুযায়ী Product-এর responsibility roughly:
stable identity
current product information
current price
whether it is available for ordering
from the product-status perspective
এখানে একটি important distinction আছে।
Product active হলেই necessarily inventory available নয়।
For example:
Product status = ACTIVE
Inventory = 0
Product catalog-এর perspective থেকে active, কিন্তু currently order করা যাবে না।
তাই product status এবং inventory আলাদা facts।
Inventory
আমাদের দুইটি possible model হতে পারত।
Option A:
Product
├── name
├── price
├── status
└── quantity
Option B:
Product
Inventory
└── product
quantity
আমাদের requirements অনুযায়ী Inventory separate concept হিসেবে model করা clearer।
কারণ Product এবং Inventory different reasons-এ change হয়।
Product:
name changes
price changes
product deactivated
Inventory:
stock added
order created
order cancelled
administrator adjustment
এগুলোর lifecycle এবং mutation pattern আলাদা।
Inventory Invariants
Inventory-এর critical rule:
quantity >= 0
Order creation-এর সময়:
requested quantity <= available quantity
Cancellation-এর সময় consumed inventory restore হবে।
এই behaviour Product metadata-এর responsibility নয়।
So:
Inventory is a separate stateful domain concept.
আমাদের current v1-এ multiple warehouse নেই।
Therefore inventory conceptually একটি product-এর current available quantity represent করে।
Is Inventory an Entity?
Inventory-এর state সময়ের সঙ্গে পরিবর্তিত হয়:
10
↓
7
↓
12
↓
9
এবং Product A-এর Inventory এবং Product B-এর Inventory আলাদা।
Current model-এ Inventory একটি product-associated stateful concept।
Implementation-এর সময় dedicated entity হিসেবে model করা sensible হতে পারে।
কিন্তু independent business identity Product বা Order-এর মতো prominent নয়।
Domain perspective থেকে সবচেয়ে important relation:
Inventory belongs to / is associated with a Product.
Product Availability vs Inventory Availability
আমাদের দুটি separate fact থাকবে:
Product is orderable
এবং:
Inventory has enough quantity
An order can proceed only when both relevant conditions satisfy হয়।
Avoid ambiguous model:
boolean available;
কারণ available বলতে unclear:
Product active?
Inventory > 0?
Enough inventory for requested quantity?
Explicit state clearer।
Order
Order আমাদের system-এর central domain entity।
Order-এর:
- stable identity আছে
- customer ownership আছে
- lifecycle আছে
- items আছে
- total আছে
- payment behaviour আছে
- cancellation rules আছে
So:
Order is a domain entity.
Order Identity
After creation:
Order #5001
আমরা later একই order:
view
pay
cancel
করতে পারি।
State change:
UNPAID
↓
PAID
হলেও identity remains:
Order #5001
Order Is More Than Data
Weak model:
class Order {
Long id;
Long customerId;
String status;
BigDecimal total;
}
এখানে arbitrary mutation easy:
order.setStatus(CANCELLED);
কিন্তু requirement বলছে:
Paid orders cannot be cancelled in v1.
Cancelled orders cannot be paid.
Meaningful behaviour better expresses intent:
order.cancel();
order.markPaid();
Exact code later implement হবে।
Important principle:
Entity should protect meaningful business state where appropriate.
Order Lifecycle
Current v1-এর জন্য আমরা শুধু required lifecycle model করব:
UNPAID
PAID
CANCELLED
Transitions:
UNPAID
├── successful payment ──▶ PAID
└── cancellation ─────────▶ CANCELLED
Invalid:
PAID ──X──▶ CANCELLED
CANCELLED ──X──▶ PAID
আমরা add করছি না:
SHIPPED
DELIVERED
REFUNDED
RETURNED
PROCESSING
PACKED
কারণ current requirements-এ এগুলো নেই।
Why UNPAID Instead of a Vague PENDING
PENDING ambiguous হতে পারে:
Pending payment?
Pending approval?
Pending fulfilment?
Our current state specifically means order exists but payment has not succeeded।
So domain language should express actual meaning clearly।
Exact enum naming implementation-এর সময় final হবে, কিন্তু vague terminology avoid করব।
Order Item
Order multiple products contain করতে পারে।
কিন্তু শুধু Product references enough নয়।
প্রতিটি ordered item-এর জন্য প্রয়োজন:
Product reference
Quantity
Purchase-time unit price
এই concept:
Order Item
Why Purchase-Time Price Belongs to Order Item
Suppose:
Product P1
Current price = €20
Customer creates:
Order O1
P1 × 2 @ €20
Later Product price becomes:
€25
Historical O1 should remain:
P1 × 2 @ €20
New order O2:
P1 × 2 @ €25
Therefore:
Product.price
=
current catalog price
while:
OrderItem.unitPrice
=
price at order creation
These are different facts।
Storing historical price is not meaningless duplication।
It preserves historical business meaning।
Should We Snapshot Product Name Too?
Current requirement specifically establishes historical pricing।
It does not say historical product name must remain unchanged।
Therefore we will not invent a requirement to snapshot every Product field into Order Item।
Current required historical fact:
purchase-time unit price
If future requirements need historical product name or description snapshots, design can evolve।
Does Order Item Need Independent Identity?
Current behaviour does not allow:
independently fetch an Order Item
independently cancel an Order Item
independently pay an Order Item
independently modify an Order Item
Order Item exists as part of Order।
Therefore its lifecycle is tied to Order।
Conceptually:
Order
├── Order Item
├── Order Item
└── Order Item
Order Item may have a database row ID later, but that does not make its identity important to the business domain।
Order Owns Its Items
This ownership is important for consistency।
Suppose order total is:
€40
If external code can arbitrarily change:
quantity 2 → 3
without Order knowing, total can become inconsistent।
Therefore:
Order should control the consistency of its items and order-level state.
This makes Order a strong candidate for an Aggregate Root।
Aggregate Thinking
We do not need a deep DDD course here, but Aggregate is useful।
An aggregate defines a consistency boundary around related domain objects।
For us:
Order
└── Order Items
is a natural aggregate।
Order is the root।
External behaviour should generally work through Order rather than independently mutating Order Items।
Product Is Not Part of the Order Aggregate
Order Item references Product, কিন্তু Order does not own Product।
Product:
- exists before the order
- appears in many orders
- changes independently
Therefore:
OrderItem
↓ references
Product
not:
Order
↓ owns
Product
Inventory Is Not Part of the Order Aggregate
Inventory also exists independently।
Many orders and admin actions may modify it।
Therefore Order should not own Inventory।
Order creation workflow coordinates:
Product
+
Inventory
+
Order
but ownership remains separate।
Why This Matters
If we placed Inventory inside Order:
Order
├── Items
└── Inventory
we would incorrectly imply one Order controls inventory lifecycle।
It doesn't।
Similarly, Product should not be copied into Order as a mutable owned entity।
Clear ownership prevents confused models।
Order Total
Order total has no independent identity।
It is based on:
Σ(orderItem.unitPrice × orderItem.quantity)
So total is value-like।
Important invariant:
Order total must remain consistent
with the recorded order items.
Whether we:
calculate total whenever needed
or:
persist the calculated total
is a later persistence decision।
Domain meaning remains the same।
Quantity
Quantity is value-like।
Current rule:
quantity >= 1
And during ordering:
requested quantity <= available inventory
Could implementation use:
int quantity;
Yes।
Could we introduce a Quantity value object?
Possibly।
But we will not introduce one solely to make the model look sophisticated।
Abstraction should earn its complexity।
Price and Money
Money needs careful technical handling।
We should not use binary floating-point values such as:
double
for exact monetary calculations।
In Java, BigDecimal is commonly suitable for decimal monetary values।
But should we create a rich:
Money(amount, currency)
value object?
Current scope explicitly excludes multiple currencies।
Therefore we should not build a generic currency framework without a requirement।
We will choose the simplest representation that preserves monetary correctness for current scope।
Payment
Payment is an important concept, but its exact domain/persistence model is not fully known yet।
Requirements tell us:
Customer can pay an eligible order.
Successful payment makes the order paid.
Failed payment must not make the order paid.
The same successful payment must not be applied twice.
Actual payment execution belongs to the external Payment Provider।
Our backend cares about:
Which order is being paid?
Did the provider confirm success?
What provider reference identifies the payment?
Was this successful result already applied?
Do We Need a Payment Entity?
Maybe.
But we should not create one prematurely।
Possible future model might require:
Payment
or:
PaymentAttempt
if we need to represent multiple attempts such as:
Attempt 1 → timeout
Attempt 2 → success
But exact provider behaviour has not yet been designed।
Therefore our current decision:
Payment is a separate domain/integration concern, but its exact persistent entity model is deferred until payment workflow design.
That is better than inventing a complicated payment system now।
Why paid = true May Be Too Weak
The simplest model could be:
boolean paid;
But external integration requirements may require more context:
provider payment reference
duplicate-success protection
previous attempt state
A boolean may eventually be insufficient।
However, we will solve this when payment design gives us enough information।
Not before।
Order and Payment Are Different Concepts
Order represents the commercial order।
Payment represents payment processing/outcome related to that order।
Successful payment can trigger:
Order
UNPAID → PAID
But Payment and Order should remain conceptually distinct।
This keeps external-integration concerns from leaking into Order domain logic।
Customer, Product, Inventory, Order, Payment: Ownership
Current conceptual ownership:
External Identity Capability
owns authentication identity
Product capability
owns current Product state
Inventory capability
owns available quantity
Order capability
owns Order lifecycle and Order Items
Payment integration/workflow
owns interaction with the external payment provider
and the payment state needed by our application
Clear ownership prevents responsibility leakage।
Current Domain Relationships
Conceptually:
Customer Identity
│
│ owns
▼
Order
│
│ contains
▼
Order Items
│
│ reference
▼
Product
│
│ has associated
▼
Inventory
Payment interaction:
Order
│
│ payment workflow
▼
Payment Boundary
│
▼
External Payment Provider
This is a domain-level view।
It is not a database schema or Java package diagram।
Entity Relationships Are Not Automatically JPA Relationships
Conceptually:
Order contains Order Items.
This does not mean we immediately write:
@OneToMany
Likewise:
OrderItem references Product.
does not automatically answer whether persistence should use:
@ManyToOne
or simply store a product identifier.
Those decisions depend on:
loading behaviour
coupling
query patterns
historical requirements
JPA behaviour
We will decide them during persistence design।
Avoid JPA-Driven Domain Design
Bad sequence:
Which `@OneToMany` do I need?
↓
Build domain model
Better:
What business relationship exists?
↓
Who owns the lifecycle?
↓
What consistency is required?
↓
How should it be persisted?
↓
Choose JPA mapping
Persistence should support the domain model, not define it prematurely।
Current Domain Invariants
Let's collect the important rules.
Product
Product has stable identity.
Product has a current price.
Product has an ordering state.
Inactive products cannot be used for new orders.
Inventory
Inventory belongs to a product context.
Inventory quantity cannot be negative.
Successful order creation cannot consume
more inventory than is available.
Order
Order has stable identity.
Order belongs to one customer.
Order contains at least one item.
Order has a valid lifecycle state.
Paid orders cannot be cancelled in v1.
Cancelled orders cannot become paid.
Order Item
Order Item references a product.
Quantity is positive.
Purchase-time unit price is preserved.
At order construction/request level:
The same product may appear at most once
in one order request.
Order Total
Order total must remain consistent
with item prices and quantities.
Customer Ownership
A customer can perform customer-scoped
operations only on their own orders.
Payment
Only an eligible unpaid order can be paid.
Failed payment does not make the order paid.
A successful payment result must not
be applied twice.
Exact implementation remains later work।
Some Invariants Cross Boundaries
Not every rule can belong entirely to one Entity।
Example:
requested order quantity
<=
available inventory
This needs both requested order information and Inventory state।
Another:
Order cancellation
→ restore inventory
touches Order and Inventory।
Therefore we need application-level workflows that coordinate independent domain concepts।
Domain Behaviour vs Workflow Coordination
A useful distinction:
Domain Entity
Knows rules about its own state।
For example:
Order.cancel()
can protect:
PAID cannot become CANCELLED.
CANCELLED cannot be cancelled again.
Inventory behaviour can protect:
quantity cannot drop below zero.
Application Workflow
Coordinates multiple concepts and dependencies।
For example cancellation:
Load order
↓
Verify ownership
↓
Ask Order to cancel
↓
Restore inventory
↓
Persist changes
The Order itself should not know:
Repository
PostgreSQL
HTTP request
Spring Security
Payment provider
Those belong outside the domain entity।
Avoid Repository Calls Inside Entities
Bad direction:
class Order {
private OrderRepository orderRepository;
private InventoryRepository inventoryRepository;
void cancel() {
// ...
orderRepository.save(this);
inventoryRepository.restore(...);
}
}
Now Order depends on infrastructure and orchestration।
Better conceptually:
Order
↓
protects order rules
while:
CancelOrder workflow
↓
coordinates Order + Inventory + persistence
This gives clearer responsibilities and easier testing।
Order Creation Workflow
Our domain model suggests an application workflow roughly like:
Identify customer
↓
Load products
↓
Verify product orderability
↓
Load/check inventory
↓
Determine current prices
↓
Construct Order + Order Items
↓
Decrease inventory
↓
Persist resulting state
Entities protect their local rules।
Workflow coordinates them।
Transaction design later ensures multi-state mutation is atomic।
Cancellation Workflow
Similarly:
Load Order
↓
Verify customer ownership
↓
Validate cancellation through Order
↓
Restore Inventory
↓
Persist changes atomically
The important design lesson:
Order owns order-state rules. The workflow owns cross-concept coordination.
Payment Workflow
Conceptually:
Load Order
↓
Verify ownership
↓
Verify payment eligibility
↓
Call Payment Provider
↓
Process confirmed result
↓
Transition Order to PAID when appropriate
But the provider is external।
Therefore local database transaction alone cannot solve every payment failure scenario।
Payment integration will receive dedicated design later।
Domain Model Should Make Invalid State Harder to Represent
A useful design goal:
Make invalid state difficult to create.
Instead of:
new Order(emptyItems);
and hoping another layer checks it, Order construction can reject empty items।
Instead of:
inventory.setQuantity(-5);
Inventory behaviour can prevent invalid quantity।
Instead of arbitrary:
order.setStatus(...);
we expose meaningful state transitions।
This is encapsulation applied to real business behaviour।
API Validation Still Matters
Domain validation does not replace boundary validation।
Suppose client sends:
{
"items": null
}
API layer can reject malformed data early।
Then domain still protects its own invariants।
Conceptually:
HTTP/API Validation
↓
Structurally valid request
↓
Domain/Application Rules
↓
Valid business state
Different boundaries protect different concerns।
Avoid Setter-Driven Domain Models
A common weak model:
order.setStatus(...);
product.setActive(...);
inventory.setQuantity(...);
allows callers to manipulate state without expressing business intent।
More meaningful operations:
order.cancel()
order.markPaid()
product.deactivate()
inventory.decrease(quantity)
inventory.increase(quantity)
communicate why state changes।
Exact API may differ later, but this is the direction we want।
Avoid Unnecessary Inheritance
Our entities all have IDs.
That does not mean:
Product extends BaseCommerceEntity
Order extends BaseCommerceEntity
Inventory extends BaseCommerceEntity
expresses useful domain meaning।
Shared technical fields are not necessarily an is-a relationship।
We should prefer explicit domain concepts and composition।
Order and Order Items Are Composition
This relationship naturally fits composition:
Order
contains
Order Items
Order Items participate in Order's total and historical record।
They do not need an independent service/API lifecycle।
We should not create:
OrderItemController
OrderItemService
just because a database table may exist later।
Orders Are Not Shopping Carts
Our requirement says:
Customer places an order.
It does not include:
Cart
Add item later
Remove item later
Change quantity after order creation
Therefore Order Items should not be modeled as freely editable cart lines।
If a future requirement introduces Cart, that will be a separate domain concept।
Current system starts at order placement।
Product Deactivation vs Deletion
Our agreed product behaviour uses deactivation rather than physical deletion।
Why?
Because historical orders may reference products।
Desired behaviour:
Product ACTIVE
↓
Product INACTIVE
Future orders:
cannot use product
Historical orders:
remain intact
We do not expose product deletion because current requirements do not need it।
Historical Meaning Belongs to Order
Once an Order is created, it represents what happened at that time।
Therefore later Product changes must not rewrite the historical transaction।
Current requirement explicitly protects purchase-time price।
This implies a broader mindset:
Order is a historical business record, not a live view of current catalog state.
We still only snapshot the data required by current requirements।
Inventory Consumption Terminology
Earlier discussion sometimes used the term "reserve inventory."
For v1, we should be precise.
Our agreed behaviour is:
Order creation
→ decreases available inventory
Eligible unpaid cancellation
→ restores inventory
We are not introducing a separate:
InventoryReservation
entity or reservation subsystem।
Because that would imply additional behaviour such as:
reservation expiry
timeout
background cleanup
which is not in current scope।
Payment Failure Does Not Automatically Cancel an Order
Current rule:
Failed payment
→ order does not become PAID
We did not establish:
Failed payment
→ automatically CANCELLED
Therefore we will not invent that behaviour।
If business later wants automatic cancellation, it becomes a new requirement।
Current Domain Decisions
We can now summarize the model.
Product
Domain entity
Owns:
- current product data
- current price
- product ordering state
Inventory
Separate stateful domain concept
Owns:
- current available quantity
- quantity invariant
Customer Identity
Externally owned identity
Used by our application for:
- order ownership
- customer authorization
We do not build identity/account management।
Order
Domain entity
Strong aggregate-root candidate
Owns:
- order lifecycle
- customer ownership reference
- order items
- historical order meaning
- order-level invariants
Order Item
Owned by Order
Represents:
- product reference
- quantity
- purchase-time unit price
No independent lifecycle in v1।
Payment
Separate domain/integration concern
Known responsibility:
- interact with external provider
- provide outcome needed by order payment workflow
Exact persistent entity shape:
deferred until payment design
Domain Model Diagram
Our current conceptual model:
┌────────────────────┐
│ Customer Identity │
└─────────┬──────────┘
│ owns
▼
┌────────────────────┐
│ Order │
│ │
│ lifecycle │
│ historical record │
└─────────┬──────────┘
│ contains
▼
┌────────────────────┐
│ Order Item │
│ │
│ product reference │
│ quantity │
│ purchase-time price│
└─────────┬──────────┘
│ references
▼
┌────────────────────┐
│ Product │
│ │
│ current price │
│ ordering state │
└─────────┬──────────┘
│ associated with
▼
┌────────────────────┐
│ Inventory │
│ │
│ available quantity │
└────────────────────┘
Payment remains an external-facing workflow:
Order
│
│ payment workflow
▼
Payment Boundary
│
▼
External Payment Provider
Review Against Requirements
Let's verify the model against our actual requirements.
Browse Products
Need:
Product
Inventory
Covered.
Create Order
Need:
Customer Identity
Product
Inventory
Order
Order Item
Covered.
Historical Pricing
Need:
Order Item purchase-time price
Covered.
Order History
Need:
Customer ownership
Order
Order Items
historical price
order creation time/order sequence
Covered conceptually.
Cancellation
Need:
Order lifecycle
Customer ownership
Inventory restoration
Covered.
Payment
Need:
Order lifecycle
Customer ownership
Payment integration boundary
Covered at the level currently justified.
Product Administration
Need:
Product lifecycle
Covered.
Inventory Administration
Need:
Inventory
Covered.
What We Deliberately Did Not Model
We did not add:
Cart
Shipment
Warehouse
Discount
Coupon
Refund
Return
Supplier
Category
Recommendation
Invoice
Inventory Reservation
Event Bus
because none of these are required by current v1।
This is good domain modeling।
The best model is not the one with the largest number of concepts।
It is the smallest model that accurately represents the current business problem।
Open Design Questions
Some questions intentionally remain for later lessons:
How exactly will Customer identity be represented locally?
How will Product and Inventory be persisted?
Should Order total be stored or derived?
What exact Java representation should Money use?
What exact Payment records are required?
How will repositories expose aggregates?
How will Order and Inventory participate
in a database transaction?
How will concurrent inventory updates be protected?
These are not missing because we forgot them।
They belong to later architecture, persistence, transaction, and payment design।
Engineering Principle
The core principle from this lesson:
A domain entity exists because its identity, lifecycle, and behaviour matter to the business—not because we need another Java class or database table.
A second principle:
Ownership must be explicit. Order owns its Order Items, but it does not own Product, Inventory, Customer identity, or the external Payment Provider.
And finally:
Model only the concepts required by the current problem.
Summary
In this lesson, we established that:
- Domain model represents business identity, state, relationships, rules, and behaviour.
- Domain Entity and JPA
@Entityare different concepts. - Product is a domain entity with current price and ordering state.
- Inventory is a separate stateful concept because its lifecycle and rules differ from Product.
- Customer identity is required for ownership, but authentication remains outside our system.
- Order is the central domain entity and a strong aggregate-root candidate.
- Order owns Order Items.
- Order Item stores product reference, quantity, and purchase-time price.
- Current Product price and historical Order Item price represent different facts.
- Order lifecycle for v1 remains intentionally small: unpaid, paid, cancelled.
- We do not model future fulfilment/refund states without requirements.
- Product and Inventory are not owned by Order.
- Cross-concept operations such as order creation and cancellation require application workflow coordination.
- Domain entities should not directly depend on repositories, HTTP, PostgreSQL, or payment-provider details.
- Payment remains a separate domain/integration concern; its exact entity shape is deferred until we know the integration requirements.
- We do not introduce Cart, Warehouse, Refund, Reservation, Kafka, or other unrelated concepts.
- The goal is not to create the most elaborate domain model; it is to represent current business behaviour accurately.
Next lesson:
Defining System Responsibilities
There we will decide which responsibilities belong to Product, Inventory, Order, Payment, Customer Context, application workflows, persistence, and HTTP boundaries, so business logic does not collapse into controllers or giant services.