Fundamentals 13 min read

Who Decides What a “Product” Means? Understanding Bounded Contexts and Ubiquitous Language

The article uses the ambiguous term “product” across product, inventory, order, and promotion teams to illustrate how bounded contexts and a ubiquitous language prevent model confusion, guide code organization, and enable safe evolution of a Spring Boot system.

Yumin Fish Harvest
Yumin Fish Harvest
Yumin Fish Harvest
Who Decides What a “Product” Means? Understanding Bounded Contexts and Ubiquitous Language

Confusion about the term “product”

In the "Preferred Store" project different departments use the word “product” to mean completely different concepts:

Product Management : SPU/SKU with title, images, category, specs, brand.

Inventory : An item in the warehouse with quantity and location, no concern for images.

Order : A snapshot at checkout – name, price, quantity – immutable after the transaction.

Promotion : A pricing unit that can be discounted or participate in full‑reduction offers.

Building a single massive Product table/class that tries to hold every field leads to:

More than 50 fields; inventory engineers cannot understand half of them.

Changing the image field breaks order‑module compilation because the same class is shared.

No one dares to modify the class for fear of breaking other modules.

This is the consequence of lacking a bounded context.

Bounded Context

Bounded Context = a clear boundary within which each term has a single, unambiguous meaning.

Applying the definition to the product example yields four bounded contexts, each with its own model:

【Product Context】   【Inventory Context】   【Order Context】   【Promotion Context】
Product               StockItem               OrderItem           PromotionItem
- title/image/category - SKU code               - product name (snapshot)   - discountable flag
- specs/brand          - available qty          - transaction price (snapshot)
- listing status      - location
                       - locked qty

Each model serves only its own context’s responsibilities and does not interfere with others. For example, the inventory context has no concept of images, and the order context stores a frozen price at the moment of purchase.

Why a large shared model gets out of control

Bounded Context Diagram
Bounded Context Diagram

Repeating class names is not inherently bad; sharing an incorrect model is. Catalog.Product and Order.OrderItem both relate to “product” but capture different business facts and should be separate.

Ubiquitous Language

Ubiquitous Language = in a bounded context, business people, product owners, developers, testers, and the code itself all use the same set of terms with identical meaning.

Examples of aligning code with the ubiquitous language:

Business says “freeze coupon”; the code provides coupon.freeze() instead of a low‑level couponMapper.updateStatus(id, 2).

Business says “order pending payment”; the code uses the enum PENDING_PAYMENT instead of a vague status = 0.

Requirement documents, database tables, Java class names, and method names all align with the same vocabulary.

Benefits

Eliminate translation loss : without a shared language, business, docs, and code each speak a different dialect, causing information loss and bugs.

Self‑explanatory code : methods like order.cancel() or coupon.freeze() are understandable even to non‑technical stakeholders, speeding onboarding.

Modeling is communication : discussing terminology surfaces business rule disagreements early.

Bounded Context vs. Subdomain

Subdomains partition the problem space (what business tasks exist); bounded contexts partition the solution space (how software models and isolates those tasks).

In the "Preferred Store" example the business sees subdomains such as product, inventory, order, promotion, member, payment, and delivery. In code each subdomain is represented by a distinct bounded context with its own model:

Product Context : title, images, category, specs, listing status.

Inventory Context : SKU, location, available stock, locked stock.

Order Context : product name, transaction price, quantity at checkout.

Ideally one subdomain maps to one bounded context, but a large subdomain may be split into multiple contexts, or a legacy system may have a context spanning several subdomains.

Implementing Bounded Contexts in Spring Boot

Two common implementation styles:

Approach A – Module/Package isolation within a monolith (recommended for starters)

Organize top‑level packages by context and forbid arbitrary cross‑package calls.

com.youxuan.store
├── order        # Order context
│   ├── domain
│   ├── application
│   └── infrastructure
├── inventory    # Inventory context
├── catalog      # Product context
├── promotion    # Promotion context
├── member       # Member context
└── payment      # Payment context
Key rule: the order package must not directly import domain objects from the inventory package. Interaction should go through events, anti‑corruption layers, or APIs – this is called a “modular monolith”.

Approach B – One microservice per context

When teams grow and contexts are truly independent, each bounded context can become its own Spring Boot microservice with its own database.

Bounded contexts are the natural basis for microservice decomposition. Do not split services by technical layers (e.g., “entity service”); split by domain contexts like “order service” or “inventory service”.
Recommendation: unless the team and business scale truly demand it, start with Approach A. Clear boundaries make later microservice extraction cheap; premature splits without clear boundaries create tangled distributed systems that are harder to maintain than a monolith.

Verifying that boundaries remain intact

The simplest check is the import statement: a context should not directly import another context’s domain model.

# Example: Order context should not import inventory domain
grep -R "import .*inventory.*domain" src/main/java/com/youxuan/store/order

# Find all cross‑context imports for manual review
grep -R "import .*(order|inventory|catalog|promotion|member|payment)." src/main/java/com/youxuan/store

If using Spring Modulith, add a module‑boundary test:

import org.junit.jupiter.api.Test;
import org.springframework.modulith.core.ApplicationModules;

class ModularityTests {
    @Test
    void verifiesModuleBoundaries() {
        ApplicationModules.of(StoreApplication.class).verify();
    }
}

This test checks for circular dependencies and illegal access to internal packages. It is not required by DDD but helps new teams enforce boundaries programmatically.

Common pitfalls

Chasing a single reusable Product class across the company is anti‑DDD. Having three seemingly duplicate classes ( Product, StockItem, OrderItem) is better than one 50‑field catch‑all.

Splitting into microservices too early, before boundaries are clear, leads to disaster. Start with a modular monolith.

Keeping the ubiquitous language only in documentation. It must be reflected in code (class names, method names, enum values) to be effective.

The seven bounded contexts (order, promotion, product, inventory, member, payment, delivery) are not islands – placing an order must deduct inventory, add points, and trigger payment. The next chapter covers context interaction without re‑coupling.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

microservicesSpring BootDDDbounded contextDomain Modelingubiquitous languagemodular monolith
Yumin Fish Harvest
Written by

Yumin Fish Harvest

A deep‑sea salvage fisherman sharing architecture insights, practical tips, and lessons learned.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.