Install
openclaw skills install bookforge-enterprise-base-pattern-catalogReference catalog for Fowler's 11 enterprise base patterns from Chapter 18 of PEAA. Use when another skill or user says 'we need a Gateway here', 'this should be a Value Object', 'replace nulls with a Special Case', 'use a Service Stub for testing', or 'separate this interface from its implementation'. Covers all 11 base patterns: Gateway pattern (wrapping external systems), Mapper pattern (decoupling subsystems), Layer Supertype (shared base class per layer), Separated Interface (dependency inversion packaging), Registry (service locator), Value Object (value-identity immutable objects), Money pattern (monetary arithmetic, no floats, allocate-by-ratio), Special Case / Null Object (replace null checks), Plugin pattern (runtime-bound implementation), Service Stub (test double for external services), Record Set (generic tabular data structure). Identifies which pattern fits a described problem, provides canonical definition and modern language parallels, distinguishes Gateway (generic external-access wrapper) from Table Data Gateway (data-access pattern), flags Registry vs DI container tradeoff, and produces a short design note with implementation sketch. Also routes to the appropriate family selector when the problem is not a base pattern.
openclaw skills install bookforge-enterprise-base-pattern-catalogThe 11 base patterns in Chapter 18 of Patterns of Enterprise Application Architecture are cross-cutting utility patterns that appear everywhere in enterprise application design. They are not architectural patterns (like Data Mapper or Front Controller) — they are the building blocks those patterns are built from. Every other PEAA skill references at least one of these.
This skill is a lookup-and-guide: identify the pattern by name or by problem description, then get the canonical definition, when to apply it, a modern language sketch, and any critical cautions.
Use this skill when:
NOT for: choosing among data-source patterns, domain logic patterns, web presentation patterns, concurrency patterns, or session state patterns. For those, see the dedicated selector skills in this set.
Accept either:
If a codebase is available, read the relevant file to produce a language-specific sketch. If not, use a stack-agnostic pseudocode sketch.
Sufficiency check: The pattern name or problem description alone is sufficient to proceed. No codebase required.
Match the input against the quick-lookup table. Accept either exact name or problem-description.
WHY: The 11 base patterns have overlapping surface descriptions (Gateway, Mapper, and Adapter all "wrap" something). Disambiguation prevents recommending the wrong pattern.
If a pattern name is given: look it up directly.
If a problem description is given: match against these trigger signatures:
| Problem signature | Pattern |
|---|---|
| "Wrap a 3rd-party / external API / resource behind a clean interface" | Gateway |
| "Decouple two subsystems so neither knows about the other" | Mapper |
| "Many classes in the same layer share common behavior" | Layer Supertype |
| "Break a dependency cycle between packages / layers" | Separated Interface |
| "Global finder / locator for widely-needed objects" | Registry |
| "Equality should be based on value, not reference; small immutable object" | Value Object |
| "Monetary amounts — avoid rounding errors, multi-currency, allocation" | Money |
| "Null checks for the same condition scattered in many places" | Special Case |
| "Swap implementation at configuration time (test vs production)" | Plugin |
| "External service is unavailable / slow — need to test without it" | Service Stub |
| "Generic tabular in-memory data structure for data-aware UI tools" | Record Set |
If multiple patterns could apply: list them with selection criteria (see Gateway vs Mapper below).
If no base pattern fits: route to the appropriate selector. Examples:
data-source-pattern-selectordomain-logic-pattern-selectorweb-presentation-pattern-selectoroffline-concurrency-strategy-selectorWHY: Routing prevents this skill from giving partial answers on topics covered better by other skills in the set.
For the identified pattern, produce:
WHY: Each component serves a different reader. The definition serves recall; the "why it exists" serves understanding; the cautions prevent misuse; the modern parallel prevents dismissing the pattern as obsolete.
Always include these cautions by pattern:
Optional<T> is not equivalent — it makes nullability explicit but doesn't eliminate branching at every call site.Output a short design note (a markdown block) suitable for pasting into a code review comment, ADR, or design doc:
## Pattern: [Pattern Name]
**Applied to:** [the specific component in the user's system]
**Why this pattern:** [one sentence connecting the problem to the pattern's intent]
**Implementation shape:**
[10-20 line code sketch in the user's language]
**Cautions:**
- [most relevant caution for this usage]
WHY: A concrete artifact makes the guidance actionable. Without a sketch, the recommendation remains abstract. The cautions prevent the most common misapplication of the pattern in this specific context.
If another base pattern complements or must be used alongside the primary recommendation:
WHY: The base patterns form a triad (Gateway + Plugin + Service Stub) and a pair (Value Object + Money). Mentioning them together prevents the user from applying one pattern while missing the pattern it depends on.
1. Gateway and Table Data Gateway are not the same. Table Data Gateway (Chapter 10) is a specific data-access pattern that accesses one database table and returns Record Sets. The generic Gateway (Chapter 18) is a broader pattern for wrapping any external resource. Every Table Data Gateway IS a Gateway, but not all Gateways are Table Data Gateways. Using "Gateway" to mean "database gateway that returns rows" is the most common PEAA vocabulary error.
WHY: Confusion between the two leads engineers to reject the generic Gateway pattern when they already know Table Data Gateway — or to misapply the pattern to a data-access context where Table Data Gateway is the correct choice.
2. Money pattern means no floats, ever.
double and float are binary floating-point types. They cannot exactly represent most decimal fractions. 0.1 + 0.1 + 0.1 != 0.3 in IEEE 754. For monetary arithmetic, use integer cents (long) or fixed-point decimal (BigDecimal, decimal, Decimal). The Money pattern wraps these with currency-awareness and safe allocation.
WHY: Float-based monetary arithmetic silently accumulates rounding errors. These errors surface as penny discrepancies in financial reports, which have compliance and audit consequences.
3. Special Case chains, while Optional terminates.
When you replace a null customer with a NullCustomer Special Case, nullCustomer.contract can return a NullContract (another Special Case) rather than null. This chain propagates polymorphism through the domain model without any null checks anywhere. Optional<T> requires the caller to handle the empty case at each .get() — it shifts the null-check burden rather than eliminating it.
WHY: The entire value of Special Case is eliminating branching. Understanding the chain behavior is essential to implementing Special Case correctly.
4. Registry is service-locator style — prefer DI injection in modern systems. Fowler himself says Registry is "global data, guilty until proven innocent" and to use it only as a last resort. Modern DI containers (Spring, .NET DI, Guice) push dependencies into constructors at wiring time, making dependencies explicit, testable, and auditable. Registry is appropriate in a narrow set of cases: deeply nested utility code that cannot be wired via constructor, or lightweight applications without a DI framework.
WHY: Registry is commonly cargo-culted from older codebases into modern ones where DI would be simpler and safer. Flagging the tradeoff prevents this.
5. Separated Interface has a cost — only use it to break specific dependencies. Each Separated Interface requires a factory with its own interface and implementation. Fowler explicitly warns against applying this to every class. For most classes, put the interface and implementation together. Separate them only when (a) you need to break a layer dependency cycle, (b) you need multiple independent implementations, or (c) you're working across team boundaries.
WHY: The pattern's benefit is targeted dependency management. Applied universally, it doubles the boilerplate without adding clarity.
Trigger: "We integrate with FedEx via their proprietary SDK. Every service class that needs shipping rates calls the SDK directly. I want to hide it behind a clean interface."
Process:
ShippingRateGateway interface + FedExShippingRateGateway implementation. The interface reflects your application's needs, not FedEx's full SDK.Output (design note):
## Pattern: Gateway
**Applied to:** FedEx shipping rate integration
**Why this pattern:** The FedEx SDK has a complex, proprietary API. Wrapping it in a
Gateway isolates all SDK coupling in one class, enables unit testing via a stub, and
makes future provider changes (FedEx → UPS) a single-class swap.
**Implementation shape (TypeScript):**
interface ShippingRateGateway {
getRates(origin: Address, destination: Address, weight: kg): ShippingRate[];
}
class FedExShippingRateGateway implements ShippingRateGateway {
getRates(origin, destination, weight) {
// translate to FedEx SDK call + translate response
}
}
class StubShippingRateGateway implements ShippingRateGateway {
getRates() { return [{ carrier: "FedEx", service: "Ground", price: Money.dollars(12.50) }]; }
}
**Cautions:**
- This is a generic Gateway (Chapter 18), not a Table Data Gateway (Chapter 10).
Table Data Gateway is for database table access specifically.
Trigger: "Our invoice totals sometimes show $0.01 discrepancies. We store monetary amounts as double in the database and in our Java domain objects."
Process:
Money class with long (cents) + Currency field. Replace all double/float money fields.allocate(ratios[]) method.Output:
## Pattern: Money
**Applied to:** Invoice total calculation
**Why this pattern:** `double` cannot represent most decimal fractions exactly in
IEEE 754. $0.01 discrepancies are the direct result. Money pattern wraps amount
(stored as integer cents) + Currency and enforces safe arithmetic.
**Implementation shape (Java):**
public record Money(long cents, Currency currency) {
public Money add(Money other) {
assertSameCurrency(other);
return new Money(cents + other.cents, currency);
}
public Money[] allocate(int[] ratios) { /* use ratio algorithm, not rounding */ }
}
**Cautions:**
- Never use double/float for monetary amounts.
- For allocation (70/30 splits): use allocate-by-ratio, not simple rounding.
Rounding 5 cents 70/30 loses or gains a penny. The ratio algorithm distributes
remainders one cent at a time.
Trigger: "When a customer hasn't been identified yet (occupant utility account), we return null from findCustomer(). Now we have null checks in 12 places, all doing the same fallback: name='Unknown', balance=0, lastBill=null."
Process:
UnknownCustomer extends Customer with default implementations. Change findCustomer() to return UnknownCustomer() instead of null. Remove the 12 null checks.unknownCustomer.lastBill should return a NullBill Special Case, not null.Output:
## Pattern: Special Case (Null Object variant)
**Applied to:** findCustomer() returning null for unidentified occupant accounts
**Why this pattern:** 12 identical null-check blocks are code duplication. Special Case
eliminates the branching by making the missing case a valid polymorphic subtype.
**Implementation shape (Python):**
class UnknownCustomer(Customer):
@property
def name(self) -> str: return "Unknown"
@property
def balance(self) -> Decimal: return Decimal("0")
@property
def last_bill(self) -> Bill: return NullBill()
def find_customer(id: str) -> Customer:
result = db.find_customer(id)
return result if result else UnknownCustomer()
**Cautions:**
- last_bill should return NullBill(), not None — chain the Special Case pattern.
- Optional[Customer] shifts branching to call sites, not eliminating it.
Special Case removes branching entirely.
references/base-patterns-cheatsheet.md — per-pattern deep-dive: full definitions, implementation sketches, modern parallels, and Fowler's cautions for all 11 patterns.Related PEAA skills that invoke base patterns:
data-source-pattern-selector — recommends Gateway (wrapping data sources) and Mapper.domain-logic-pattern-selector — recommends Service Stub for testing domain logic, Value Object, Special Case.object-relational-structural-mapping-guide — covers Embedded Value (persistence strategy for Value Object, Money).distribution-boundary-designer — recommends Separated Interface for distribution boundaries.web-presentation-pattern-selector — references Plugin for theme/environment-based view selection.This skill is licensed under CC-BY-SA-4.0. Source: BookForge — Patterns of Enterprise Application Architecture by Martin Fowler, David Rice, Matthew Foemmel, Edward Hieatt, Robert Mee, Randy Stafford.
Install related skills from ClawhHub:
clawhub install bookforge-data-source-pattern-selectorclawhub install bookforge-domain-logic-pattern-selectorclawhub install bookforge-distribution-boundary-designerclawhub install bookforge-object-relational-structural-mapping-guideOr install the full book set from GitHub: bookforge-skills