A logbook for curious waters
← Software Engineering Field Guide

Ubiquitous Language and Bounded Context

Before you begin: Data models, classes, and communication between modules.

A ubiquitous language is a shared vocabulary used by domain experts and developers in discussion, tests, and code. A bounded context defines where one particular model and vocabulary apply. Outside that boundary, another team may legitimately use the same word differently. 1

“Use consistent names” is only half the lesson. The other half is recognizing where forcing one meaning on everyone would create confusion.

Two teams say “order” and mean different things

In sales, an order includes purchased items, prices, and payment status. In shipping, the immediate concern is an instruction to dispatch parcels. Shipping should not need to interpret sales’ internal payment-state flags just to understand its work.

For this deliberately simplified shop, each purchased unit becomes one parcel and only paid orders can be handed off. That packing rule is an example assumption, not a general rule of commerce.

Sales modelHandoffShipping model
Purchased unit countConvert using the shop’s packing ruleParcel count
Payment statusCheck readiness before handoffNo payment-state field required
Sales order identifierRetain a correlation identifierLink back to the purchase
public sealed record SalesOrder(string Id, int PurchasedUnits, bool Paid);
public sealed record DispatchRequest(string OrderId, int Parcels);

public static class Handoff
{
    public static DispatchRequest ForShipping(SalesOrder order)
    {
        if (!order.Paid)
        {
            throw new InvalidOperationException("Payment required before dispatch.");
        }
        return new DispatchRequest(order.Id, order.PurchasedUnits);
    }
}
var sale = new SalesOrder("O-7", 2, true);
DispatchRequest dispatch = Handoff.ForShipping(sale);
Console.WriteLine(dispatch.Parcels); // 2

The two records are not aliases for one enormous shared Order. The handoff explicitly translates the information shipping needs. Future changes to sales’ pricing model need not expand the dispatch request.

from dataclasses import dataclass


@dataclass(frozen=True)
class SalesOrder:
    id: str
    purchased_units: int
    paid: bool


@dataclass(frozen=True)
class DispatchRequest:
    order_id: str
    parcels: int


def for_shipping(order):
    if not order.paid:
        raise RuntimeError("Payment required before dispatch.")
    return DispatchRequest(order.id, order.purchased_units)
sale = SalesOrder("O-7", 2, True)
dispatch = for_shipping(sale)
print(dispatch.parcels)  # 2

The conversion is a visible place to discuss the business relationship. Assume validated positive unit counts; the dataclass annotations do not validate external input automatically.

A boundary is more than a folder name

The examples demonstrate model translation, not deployment isolation or durable messaging. The two contexts could live inside one application or in separate services. A bounded context is a boundary of model meaning, not automatically a microservice or one database.

Document which side owns each rule and what crosses the boundary. A context map can describe relationships between teams and models. A shared identifier can connect related records without requiring a single shared class containing every team’s fields.

Within sales, agree what “paid,” “confirmed,” and “cancelled” mean. Use those words consistently in operations and tests. Do not quietly use “confirmed” for payment in one method and inventory reservation in another. A glossary helps only when the model and actual conversations follow it.

Across contexts, translation should preserve meaning rather than merely copy similarly named fields. If shipping later packs several units into a parcel, this handoff must change; renaming PurchasedUnits to Parcels would not solve the mismatch.

These boundaries should emerge from real differences in language, rules, and ownership. Splitting a small, coherent feature into many contexts can create needless integration work. The objective is local clarity with explicit relationships—not universal terminology imposed on incompatible models.

Sources

  1. Martin Fowler — Bounded Context, on locally unified models and their relationships.
  2. Martin Fowler — Ubiquitous Language, on language shared between domain experts and developers.