Before separating the software, ZibiWorks needs to understand the business boundaries hidden inside its monolith.
This article is part of ZibiWorks, an ongoing fictional modernization case study. The ZibiWorks site is the canonical home of the series, with the full company context, starting architecture, and related installments. Explore the full series at ZibiWorks.
We've chosen Fulfillment. But what is it?
ZibiWorks decided to evolve its platform rather than replace it, and picked
Fulfillment as the first target. The obvious next step is to find the Fulfillment
code and pull it out. But before doing that, the team needs to establish what
actually belongs to Fulfillment.
In the current application, ordering, inventory, and fulfillment are tightly
woven together. They share classes, business logic, and the same Order model.
Extract everything that touches that model and the move drags along far more than
Fulfillment; extract too little and the new service still depends on the monolith
for decisions it should own. Before moving any code, the team has to understand
how the business actually works.
Start with the business, not the database
That understanding comes from the people who run the work, not from reading the
schema. Each business function that participates in an order has its own
perspective on it. ZibiWorks brings together subject matter experts from Order
Management, Inventory, and Fulfillment, along with business analysts and software
architects. Rather than starting with
the existing application, the group walks through several business scenarios: a
straightforward order, a return, a cancellation already in progress, and the
example below. The goal isn't to document every workflow, but to uncover where
responsibilities, decision ownership, and the meaning of common terms begin to
diverge.
One of those scenarios exposes the problem particularly well. A customer orders
three of the same product. Only two are on hand; the third is out of stock. That
single situation pulls the groups apart. Inventory can reserve two units and has to decide what the third
reservation even means with no stock behind it. Fulfillment can pick and pack the
two available units now, but only if a partial shipment is allowed. Order
Management owns that call, because splitting one order into two deliveries touches
pricing and the customer commitment. Nobody can answer alone, and each is
answering a different part of the question.
That disagreement is the useful part. The same word starts to mean different
things depending on who is saying it. Take available:
| Business perspective | What "available" means |
|---|---|
| Order Management | The customer is allowed to buy it; it is sellable right now |
| Inventory | Physical stock is on hand and not already reserved |
| Fulfillment | The item is somewhere it can actually be picked and packed |
None of these is sloppy or wrong. A product can be available to sell while there
is no stock to reserve, which is precisely how a backorder happens. The word is
only unambiguous inside one context.
It is tempting to treat these differences as a data problem and add more statuses
or flags to the shared Order model. But that doesn't resolve the underlying issue.
The business functions have different rules and make different decisions. More
fields won't establish who owns those decisions.
Candidate boundaries
Domain-Driven Design gives the team a useful concept: the bounded context. A
bounded context defines where a particular business model, its rules, and its
language have consistent meaning. That is exactly why available can carry three
different meanings at once without any of them being wrong: each belongs to a
different context.
The differences in language aren't just vocabulary problems. They point to
different rules, responsibilities, and decisions. So the team draws its candidate
boundaries from those rules, language, and decision ownership, not by copying the
org chart into the architecture.
Key Concept
Ubiquitous language needs to be consistent within a bounded context, not across the entire business.
The conversations point to three candidate bounded contexts: Order Management,
Inventory, and Fulfillment.
Order Management
- The customer's commercial transaction
- Orders, order lines, pricing, commitments
- Cancellations and the order lifecycle
Inventory
- Availability and commitment of stock
- SKUs, on-hand quantities, reservations
- Replenishment and warehouse locations
Fulfillment
- The operational work to satisfy an order
- Pick lists, packing, shipment preparation
- Handoff, dispatch, exceptions
A fourth candidate shows up as the group follows a package out the door. Getting
items picked and packed is Fulfillment's work. Moving the parcel after that,
transportation itself, is a different kind of concern with different experts and
a different model.
Fulfillment
- Picking and packing items
- Preparing shipments
- Managing fulfillment work and handoff
Shipping
- Carrier selection and transportation
- Route optimization and delivery exceptions
- Fleet maintenance, where applicable
- Delivery tracking in transit
ZibiWorks does not run trucks. It hands parcels to Handle With Care, an
external carrier, so most of that right-hand list is the carrier's business, not
ZibiWorks'. The open question is whether ZibiWorks has enough Shipping rules and
decisions of its own to justify a separate bounded context, or whether Shipping is
primarily an integration concern. How that integration is implemented is a
separate architectural decision we'll address later. For now, Shipping remains a
candidate worth investigating, and the focus stays on Fulfillment.
What belongs to Fulfillment
Working from the conversations rather than the schema, the team sorts
responsibilities by who owns each decision.
| Responsibility | Business owner |
|---|---|
| Capture and manage customer orders | Order Management |
| Handle commercial cancellations | Order Management |
| Maintain stock availability | Inventory |
| Reserve and allocate stock | Inventory |
| Decide how accepted order lines are fulfilled | Fulfillment |
| Manage picking and packing | Fulfillment |
| Prepare shipments and hand off to the carrier | Fulfillment |
| Report fulfillment progress | Fulfillment |
Naming an owner for each decision does not remove the need to collaborate.
Cancellation is the clearest case. Order Management owns the decision, but it
cannot make it alone: whether a cancellation is even possible depends on how far
the physical work has gone, and that fact belongs to Fulfillment. Neither context
takes over the other's rules, and the decision still needs both.
Order Management owns the decision and the commercial rules; Fulfillment owns the operational facts. The diagram is conceptual: it says who decides what, not how the two contexts talk to each other.
That is the shape of a real boundary: collaboration without shared ownership.
One order, several lifecycles
The product owner asks the fair question: if Fulfillment separates from Order
Management and Inventory, how does a customer check their order? They do not want
three status screens.
A coherent customer view does not require one context to own the whole order.
Take the three-item order: Order Management knows it was accepted, Inventory knows
two units are reserved and one is not, Fulfillment knows the available items are
packed and on their way. The customer-facing view can present information from
all three contexts without becoming authoritative for their underlying business
state. Whether ZibiWorks assembles that information on demand or maintains a
separate read model is a decision for a later installment. The read view is
derived; the owning contexts remain the source of truth.
A context owns its data. That doesn't mean it's the only context that needs access to it.
Now look at the monolith
The business analysis produced a clean map of responsibilities. The next step is
to hold it up against the existing application and see how much of it the code and
data actually reflect. The answer, in the legacy Orders table, is not much. A
single row has accumulated fields from several parts of the business at once.
The same row holds Order Management, Inventory, and Fulfillment fields, with the two shipping fields still unassigned. The business drew three clean boundaries; the table drew none.
The shape made sense once. ZibiWorks shipped from one warehouse, an order was one
shipment, and the path from placed to shipped was close to linear, so putting it
all on the order record was the simplest thing that worked. Then the business
changed. More warehouses broke the single WarehouseId. Partial shipments turned
one order into several deliveries. Backorders, specialized carriers, and more
involved inventory commitments each needed somewhere to live, and the fastest
place was another column on a table everything already understood. No single one
of those decisions was wrong; added up, they left the Order model owning work
from three different parts of the business, and a fourth knocking at the door.
The database reflects years of implementation decisions, not necessarily where the business boundaries belong.
Deciding what moves
The business boundaries turn the extraction choice into something the team can
defend rather than guess at.
| Candidate bounded context | Decision | Reason |
|---|---|---|
| Fulfillment | Extract | The scalability and resilience pressures that started the modernization |
| Order Management | Remain | Commercial workflows are stable and were never the problem |
| Inventory | Remain | Reach extends well beyond this effort; extracting it now blows up the scope |
| Shipping | Investigate | Still an open question, not an extraction commitment |
Picking Fulfillment is the easy part. The risk is moving the code without
achieving the separation. Fulfillment still needs stock reserved by Inventory and
order and cancellation decisions from Order Management, and today it reaches all
of that through the shared Order row. Give it its own service but leave those
dependencies in place, and Fulfillment spends its life calling back into the
monolith: slower than the method calls it replaced, still hostage to legacy data
and release schedules, and buying little of the scalability and resilience that
justified the move. Moving the code into its own service doesn't mean you've
removed the coupling. The business decision is settled; making the separation
real is the part still to work out.
The boundary before the seam
ZibiWorks now understands its business boundaries, and it knows the
implementation does not honor them. That gap is the real result of this
installment. Knowing what to separate tells ZibiWorks what Fulfillment must stop
sharing, but not where the running system can actually be cut or how Fulfillment
gets what it needs once it is. That is the next problem.
Key Takeaway
The business boundary establishes what must be separated. The technical seam establishes where and how.
ZibiWorks is fictional. The ideas and opinions expressed throughout the series are my own and do not represent the views, positions, or practices of my employer.


Top comments (0)