This document describes the bounded contexts in our library system and how they relate to each other. Understanding these relationships is crucial for maintaining clean boundaries and managing dependencies.
A Context Map is a DDD strategic pattern that visually documents:
- The bounded contexts in a system
- The relationships between them
- How they communicate and integrate
Purpose: Manages the library's book catalog - bibliographic records.
Key Concepts:
CatalogBook- A bibliographic record (title, author, ISBN)CatalogBookId- Unique identifier shared with other contextsTitle,Author,ISBN- Value objects
Responsibilities:
- Adding books to the catalog
- Managing book metadata
- Removing books from the catalog
Does NOT know about:
- Whether a book is borrowed
- Who has borrowed it
- Due dates or loans
Purpose: Manages library members (patrons).
Key Concepts:
Patron- A library memberPatronId- Unique identifier shared with other contextsMembershipTier- Determines borrowing privilegesEmailAddress- Contact information (from Shared Kernel)
Responsibilities:
- Member registration
- Managing membership tiers
- Suspending/reinstating members
Does NOT know about:
- Specific loans or borrowed books
- Due dates
Purpose: The heart of the library - managing book loans.
Key Concepts:
Loan- A book being borrowed by a patronLoanableBook- Lending's view of a book (availability focused)DueDate- When the book must be returnedBorrowerInfo- Lending's view of a patron (ACL translation)BookReference- Lending's view of catalog data (ACL translation)
Responsibilities:
- Creating and managing loans
- Tracking due dates
- Handling returns
- Managing overdue items
Dependencies:
- References
CatalogBookIdfrom Catalog - References
PatronIdfrom Patron - Uses ACL to translate upstream data
Purpose: Handles all outbound communications.
Key Concepts:
- Email notifications
- Templates
- Delivery tracking
Responsibilities:
- Sending email notifications
- Managing templates
- Logging delivery status
┌─────────────────────────────────────────────────────────────────────┐
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Catalog │ │ Patron │ │
│ │ Context │ │ Context │ │
│ │ │ │ │ │
│ │ (Upstream) │ │ (Upstream) │ │
│ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │
│ │ Published │ Customer- │
│ │ Language │ Supplier │
│ │ (Events) │ │
│ ▼ ▼ │
│ ┌──────────────────────────────────────────────┐ │
│ │ │ │
│ │ Lending Context │ │
│ │ (Core Domain) │ │
│ │ │ │
│ │ ┌─────────────────────────────────────┐ │ │
│ │ │ Anti-Corruption Layer (ACL) │ │ │
│ │ │ ┌─────────────┐ ┌─────────────┐ │ │ │
│ │ │ │ CatalogACL │ │ PatronACL │ │ │ │
│ │ │ └─────────────┘ └─────────────┘ │ │ │
│ │ └─────────────────────────────────────┘ │ │
│ │ │ │
│ └──────────────────┬───────────────────────────┘ │
│ │ │
│ │ Published Language │
│ │ (Domain Events) │
│ ▼ │
│ ┌──────────────────────────────────────────────┐ │
│ │ Notification Context │ │
│ │ (Generic Subdomain) │ │
│ │ │ │
│ │ Subscribes to: │ │
│ │ - BookBorrowed │ │
│ │ - BookOverdue │ │
│ │ - BookReturned │ │
│ └──────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ Shared Kernel │ │
│ │ │ │
│ │ - AggregateRoot │ │
│ │ - DomainEvent │ │
│ │ - EmailAddress │ │
│ └──────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
Pattern: Open Host Service with Published Language
The Catalog context publishes events (BookAddedToCatalog, BookRemovedFromCatalog) that other contexts can subscribe to. The event schema is the "published language" - a contract that Catalog commits to maintaining.
# Catalog publishes this event
@dataclass(frozen=True)
class BookAddedToCatalog(DomainEvent):
book_id: str
title: str
author: str
# Lending subscribes and creates its own LoanableBookPattern: Customer-Supplier
Lending (downstream/customer) depends on Patron (upstream/supplier) for borrower information. The Patron context agrees to provide what Lending needs.
The ACL translates Patron's model to Lending's:
# Patron's model
class Patron:
name: PatronName
membership_tier: MembershipTier
# Lending's view (via ACL)
@dataclass
class BorrowerInfo:
name: str
borrowing_limit: int
loan_duration_days: intPattern: ACL
Lending uses an Anti-Corruption Layer to protect itself from changes in upstream contexts. If Catalog or Patron change their models, only the ACL implementation needs updating.
# ACL Interface (in domain)
class PatronACL(Protocol):
async def get_borrower_info(self, patron_id: str) -> Optional[BorrowerInfo]:
...
# ACL Implementation (in infrastructure)
class PatronACLAdapter:
def __init__(self, patron_repository: PatronRepository):
self.patron_repo = patron_repository
async def get_borrower_info(self, patron_id: str) -> Optional[BorrowerInfo]:
patron = await self.patron_repo.get_by_id(patron_id)
if not patron:
return None
# Translate Patron's model to Lending's BorrowerInfo
return BorrowerInfo(
patron_id=patron.id.value,
email=patron.email.value,
name=patron.name.full_name,
can_borrow=patron.can_borrow(current_loans),
borrowing_limit=patron.membership_tier.borrowing_limit,
loan_duration_days=patron.membership_tier.loan_duration_days,
)Pattern: Shared Kernel
Some types are shared across all contexts by agreement. Changes to the shared kernel require coordination between all teams.
Shared types:
AggregateRoot- Base class for all aggregatesDomainEvent- Base class for all eventsEmailAddress- Used by Patron and Notification
1. API receives borrow request
└─→ BorrowBook use case (Lending context)
2. Lending calls PatronACL.get_borrower_info(patron_id)
└─→ ACL fetches from Patron context
└─→ ACL translates to BorrowerInfo
3. Lending calls CatalogACL.get_book_reference(book_id)
└─→ ACL fetches from Catalog context
└─→ ACL translates to BookReference
4. Lending creates Loan aggregate
└─→ Loan.create() raises BookBorrowed event
5. Unit of Work commits transaction
└─→ Dispatches BookBorrowed event to message broker
6. Notification context receives BookBorrowed
└─→ Sends confirmation email to patron
- Isolation: Each context can evolve independently
- Clear ownership: Each team owns their context
- Explicit dependencies: ACL makes dependencies visible
- Flexible integration: Events enable loose coupling
- Focused models: Each context has its optimized model
- New features in Lending? Keep them within the Lending context
- Need Patron data in Lending? Add to PatronACL, not direct access
- New context needed? Define relationships in this map first
- Changing shared kernel? Coordinate with all context owners