Theoretical Foundations
Welcome to the curriculum workspace. Here you will find long-form technical guidelines outlining core architectural blueprints and implementation mechanics.
Module 15: Environmental Assessment (Greenfield vs. Brownfield)
PREREQUISITE STATEMENT: Read this module after completing Module 14 (Fault Tolerance). Building resilient codebases is half the challenge; the other half is deploying them within complex, pre-existing enterprise environments. This module teaches you how to decompose legacy monoliths without disrupting active production operations.
1. Greenfield vs. Brownfield Architectures
Software architects rarely work with a blank slate. Most enterprise engineering occurs within the context of existing legacy systems:
[ Greenfield Architecture ]
- Absolute design freedom
- No technical debt
- Risk: Analysis paralysis, lack of real operational baseline
[ Brownfield Architecture ]
- Constrained by legacy databases and code
- Must maintain active revenue lines (100% uptime)
- Risk: Technical debt, leaky abstractions, regression bugs
A. Greenfield Engineering
Greenfield engineering refers to developing a system from scratch, with no pre-existing legacy constraints, schemas, or running servers:
- The Opportunity: Absolute design freedom. You can select the modern frameworks, databases, and deployment pipelines (e.g., Serverless, Kubernetes) that perfectly align with your target scalability goals.
- The Risks:
- Analysis Paralysis: The lack of constraints can lead to over-engineering and delayed delivery times.
- Unknown Operational Profile: You have no historical data regarding user load spikes, database query profiles, or cache hit ratios. Architects must design based on speculative assumptions.
B. Brownfield Transformation
Brownfield transformation refers to modifying, extending, or replacing a live legacy system:
- The Constraint: The legacy monolith is actively serving users and generating company revenue. You cannot shut down operations for a rewrite. You must migrate the architecture incrementally — equivalent to "changing the engine of a plane while it is flying."
- The Risks:
- Data Integration Bottlenecks: Legacy databases are often un-normalized, lacking foreign key constraints, and coupled to multiple systems.
- Regression Failures: Lack of automated test coverage in legacy systems makes modifying code risky.
- Leaky Abstractions: It is easy for legacy design patterns to leak into new microservices, corrupting the clean domain models.
2. The Strangler Fig Pattern
To decompose a legacy monolith safely, architects use the Strangler Fig Pattern. Named after the strangler fig tree that seeds in the upper branches of a host tree, grows roots down to the ground, and eventually envelope and kill the host, this pattern replaces monolithic capabilities incrementally:
Phase 1 (Interception):
[ Client ] ---> [ Routing Gateway ] ---> [ Legacy Monolith ]
Phase 2 (Co-existence):
[ Client ] ---> [ Routing Gateway ] ---> [ Legacy Monolith ] (User/Billing queries)
---> [ Order Service ] (Orders queries)
Phase 3 (Strangled):
[ Client ] ---> [ Routing Gateway ] ---> [ Order Service ]
---> [ User Service ]
(Legacy Monolith Decommissioned)
The Strangler Migration Lifecycle:
- Phase 1: Ingress Interception: Insert a Routing Gateway (e.g. Envoy, NGINX, or a Cloud CDN) in front of the Legacy Monolith. Initially, configure the gateway to route 100% of public traffic to the monolith.
- Phase 2: Extract & Co-exist: Identify a cohesive business capability (such as
/orders) to extract. Build a new microservice (OrderService) with its own database. Update the Routing Gateway to route requests targeting/ordersto the new service, while all other requests (e.g./users,/billing) continue to hit the monolith. - Phase 3: Strangling: Extract subsequent capabilities (e.g.
/userstoUserService,/billingtoBillingService) one by one, shifting routing targets in the gateway. - Phase 4: Monolith Elimination: Once all capabilities have been migrated, the legacy monolith receives zero traffic and can be decommissioned.
3. The Anti-Corruption Layer (ACL) Pattern
When a newly extracted microservice needs to communicate with the legacy monolith to fetch data, we face a data modeling challenge. Legacy systems often represent data in messy formats (e.g., a single string column containing delimited user details, or archaic naming conventions).
If the new service adopts these schemas directly, its clean domain model is corrupted. To prevent this, place an Anti-Corruption Layer (ACL) between the new service and the legacy monolith:
[ New Order Service ] ---> [ Anti-Corruption Layer (Translation) ] ---> [ Legacy Monolith API ]
The ACL translates data structures between the legacy representation and the clean domain models of the new service.
Example: TypeScript Anti-Corruption Layer implementation
Below is a clean domain representation and an ACL translating a legacy address string structure into a structured domain value object:
// Clean Domain Value Object
interface CustomerAddress {
street: string;
city: string;
postalCode: string;
}
// Legacy Monolith API Output
interface LegacyAddressPayload {
// Legacy format: "123 Main St, Dublin, D02, IE"
raw_address_line: string;
country_code: string;
}
// Anti-Corruption Layer (ACL) Translator
class AddressACLTranslator {
public static toDomain(legacyPayload: LegacyAddressPayload): CustomerAddress {
const parts = legacyPayload.raw_address_line.split(",");
return {
street: parts[0]?.trim() || "Unknown",
city: parts[1]?.trim() || "Unknown",
postalCode: parts[2]?.trim() || "Unknown"
};
}
}
4. Tooling: Legacy System Assessment
Before executing a brownfield migration, architects use static analysis and mapping tools to discover dependencies:
- Dependency Structure Matrix (DSM): Tools like Lattix or Structure101 analyze codebases to create a matrix mapping package dependencies. This highlights cycles and tightly coupled classes that must be decoupled before extraction.
- OpenTelemetry Tracing: Deploying tracing agents (e.g., Datadog, Jaeger) to the legacy monolith maps the real-world runtime execution paths, identifying database tables that are shared across different domains.
5. Documentation Standard: Legacy Modernization Ledger
A Legacy Modernization & Technical Debt Valuation Ledger documents the codebase sections selected for migration, estimating the technical debt cost and defining the replacement strategy:
Technical Debt Ledger
| Monolith Code Module | Technical Debt Risk | Cyclomatic Complexity | Business Impact | Chosen Migration Pattern | Estimated Migration Effort | Target Microservice |
|---|---|---|---|---|---|---|
com.legacy.orders |
High (Slow billing runs, lock errors) | 84 (High loop nesting) | Outages block client revenue | Strangler Fig with dynamic proxy routing. | 6 Weeks | orders-service |
com.legacy.users |
Medium (Static schema, slow joins) | 32 | Prevents user logins | Strangler Fig with Conformist integration. | 4 Weeks | user-service |
com.legacy.reporting |
Low (Batch reporting runs overnight) | 12 | Non-critical reporting delay | Direct rewrite to Serverless Cron task. | 2 Weeks | reports-lambda |
6. Hands-on Architecture Challenge
Scenario Description
A client application (ClientBrowser) connects directly to a LegacyMonolith. The monolith database handles both User Profiles and Order Transactions. You must refactor this architecture to implement the Strangler Fig Pattern with an Anti-Corruption Layer.
Your Goal:
- Insert an
APIGatewaybetween theClientand the services. - Define the routing paths inside the
APIGateway:- Route legacy endpoints (e.g.,
/users) to theLegacyMonolith. - Route refactored endpoints (e.g.,
/orders) to a newOrderMicroservice.
- Route legacy endpoints (e.g.,
- Inside the
OrderMicroservice, introduce anAntiCorruptionLayer(ACL) block. - Show the
AntiCorruptionLayercommunicating with theLegacyMonolithto fetch legacy user profile dependencies and translating them. - Draw this migration architecture using the diagram editor's graph syntax.
7. Practice Challenge Template
Use this template in your sandbox to model the Strangler Fig migration:
graph TD
subgraph Legacy_State [Legacy Direct Access]
ClientDirect[Client Browser] -->|Direct HTTP Calls| MonolithDirect[Legacy Monolith]
style ClientDirect fill:#faa,stroke:#333,stroke-width:2px
style MonolithDirect fill:#faa,stroke:#333,stroke-width:2px
end
subgraph Strangler_State [Strangler Fig Co-existence]
Client[Client Browser] -->|1. HTTP Request| Gateway[Routing Gateway]
Gateway -->|2a. Route /users| LegacyMonolith[Legacy Monolith]
Gateway -->|2b. Route /orders| OrderService[Order Microservice]
subgraph OrderService_Boundary [Order Service Boundary]
OrderService -->|3. Query Legacy Profile| ACL[Anti-Corruption Layer]
ACL -->|4. Translate Schema| CleanDomain[Order Domain Logic]
end
ACL -->|5. HTTP API Call| LegacyMonolith
style Client fill:#9f9,stroke:#333,stroke-width:2px
style Gateway fill:#9ff,stroke:#333,stroke-width:3px
style LegacyMonolith fill:#faa,stroke:#333,stroke-width:2px
style OrderService fill:#9f9,stroke:#333,stroke-width:2px
style ACL fill:#9ff,stroke:#333,stroke-width:2px
style CleanDomain fill:#9f9,stroke:#333,stroke-width:2px
end
NEXT MODULE BRIDGE: Defining technical migration patterns establishes the physical implementation rules, but driving organizational buy-in and documenting key engineering decisions requires architectural governance. Proceed to Module 16: Governance, ADRs, & The Architecture Review Board (ARB) to discover how to align engineering teams and record architectural choices.