Designing System Architecture Patterns

1. Designing Layered Architecture

LayerResponsibilityExample
PresentationUI / API endpointsREST controllers
ApplicationUse case orchestrationService classes
DomainBusiness rules / entitiesDomain models
InfrastructureDB, queues, external APIsRepositories, adapters
Note: Dependencies flow downward only. Easy to learn but couples layers tightly; risk of anemic domain models.

2. Designing Hexagonal Architecture

ElementRole
Core (domain)Business logic, no I/O dependencies
PortsInterfaces defined by core (in/out)
AdaptersImplementations: REST, JPA, Kafka
Driving sideInbound (HTTP, CLI, tests)
Driven sideOutbound (DB, MQ, third-party)

Example: Hexagonal port/adapter (Java)

// Port (defined in core)
public interface OrderRepository {
    Optional<Order> findById(OrderId id);
    void save(Order order);
}

// Adapter (infrastructure)
@Repository
class JpaOrderRepository implements OrderRepository {
    private final OrderJpaRepo jpa;
    public Optional<Order> findById(OrderId id) { return jpa.findById(id.value()).map(this::toDomain); }
    public void save(Order order) { jpa.save(toEntity(order)); }
}

3. Designing Clean Architecture

Ring (inner→outer)Contents
EntitiesEnterprise business rules
Use CasesApplication-specific business rules
Interface AdaptersControllers, presenters, gateways
Frameworks & DriversWeb, DB, UI, devices
Note: Dependency rule — source code dependencies point only inward. Inner layers know nothing about outer layers.

4. Designing Onion Architecture

RingPurpose
Domain ModelEntities + value objects
Domain ServicesCross-aggregate logic
Application ServicesOrchestration / use cases
Infrastructure / UI / TestsOuter; depend on inner only

5. Designing Event Sourcing Architecture

ConceptDescription
Event StoreAppend-only log of state changes
AggregateRehydrated by replaying events
SnapshotPeriodic state cache to speed replay
ProjectionRead model built from events
ProsAudit trail, time travel, easy CQRS
ConsSchema evolution, eventual consistency

Example: Event-sourced aggregate

class Account {
    private long balance;
    private final List<Event> uncommitted = new ArrayList<>();

    void deposit(long amount) {
        apply(new MoneyDeposited(amount));
    }
    void apply(Event e) {
        if (e instanceof MoneyDeposited d) balance += d.amount();
        uncommitted.add(e);
    }
    static Account rehydrate(List<Event> history) {
        var a = new Account();
        history.forEach(a::apply);
        a.uncommitted.clear();
        return a;
    }
}

6. Designing CQRS Architecture

SideOptimized ForStorage
CommandWrites, validation, business logicNormalized OLTP, event store
QueryReads, denormalized projectionsRead replica, ES, materialized view
Note: Sync via events. Adds eventual consistency; only adopt when read/write loads diverge significantly.

7. Designing Serverless Architecture

ComponentServiceUse Case
FunctionsLambda, Cloud FunctionsEvent handlers, APIs
API GatewayAPI GW, Cloud EndpointsHTTP routing, auth
Event BusEventBridge, Pub/SubDecoupled triggers
StorageDynamoDB, Firestore, S3Managed, auto-scaling
LimitsCold start, 15-min timeout, vendor lock

8. Designing Service-Oriented Architecture (SOA)

AspectSOAMicroservices
CommunicationESB, SOAPLightweight HTTP/gRPC
DataOften shared DBsDB-per-service
GranularityCoarseFine
GovernanceCentralizedDecentralized

9. Designing Lambda Architecture

              ┌──────────────┐
   Source ──▶│ Batch Layer  │──▶ Batch Views ─┐
        │    └──────────────┘                  │──▶ Serving Layer ──▶ Query
        └──▶ Speed Layer ──▶ Realtime Views ──┘
      
LayerTechTrade-off
BatchSpark, HadoopAccurate, high latency
SpeedFlink, Kafka StreamsLow latency, approximate
ServingDruid, HBaseMerges batch+speed views

10. Designing Kappa Architecture

PropertyKappa
LayersStream-only (no batch)
ReprocessingReplay log from offset 0
TechKafka + Flink/Kafka Streams
ProSingle codebase, simpler than Lambda
ConRequires durable, replayable log

11. Designing Modular Monolith Architecture

AspectApproach
DeploymentSingle artifact
ModulesStrict package boundaries, no cross-imports
CommunicationIn-process events / interfaces
DataSchema-per-module in shared DB
Migration pathExtract module → microservice when needed
Note: Best starting point for most products — get microservices benefits without distributed system tax.

12. Designing Strangler Fig Pattern

Strangler Migration Steps

  1. Place a facade/proxy in front of legacy system
  2. Build new functionality in modern stack behind facade
  3. Route specific endpoints to new system
  4. Migrate data incrementally (dual-write or CDC)
  5. Decommission legacy modules as they are replaced
RiskMitigation
Data syncCDC + reconciliation jobs
Stalled migrationTime-box, set kill date for legacy
Dual maintenanceFreeze legacy features