CMPE273 · Module 4

Architecture Styles for Enterprise Distributed Systems

Compare layered systems, microservices, and event-driven systems through a campus marketplace. Design integration boundaries, contain failures, and develop a team project proposal.

Learning Objectives

  1. Distinguish layers, tiers, service boundaries, and failure domains.
  2. Compare layered, microservice, and event-driven designs using one workflow.
  3. Specify an integration contract and predict failure propagation.
  4. Propose a team project with measurable failure experiments.

Core Concepts

Layer and tier

A layer separates code responsibilities. A tier separates runtime deployment.

Business capability

A cohesive responsibility with an owner, rules, and a contract.

Domain event

A fact about a completed business change, with documented meaning.

Integration boundary

An agreement about data meaning, ownership, trust, and failure behavior.

Failure domain

Components that can fail together because they share a dependency or resource.

Diagrams

Clear responsibilities make checkout easier to change

flowchart LR
 U[HTTP handler] --> A[Checkout application]
 A --> D[Domain rules]
 A --> R[Repository]
 R --> DB[(Database)]

The handler parses a request. The application coordinates checkout, domain code checks rules, and the repository persists state. These calls may all run in one process.

Code layers inside a deployment tier

flowchart LR
 B["Browser tier"] -->|"Network request"| H
 subgraph APP["Application tier: one running application"]
 H["Request handling layer"] --> L["Business logic layer"]
 L --> D["Data access layer"]
 end
 D -->|"Network query"| DB[("Database tier")]

The three boxes inside the application are code layers using local calls. The browser, application, and database are deployment tiers connected by network calls. A layer does not need its own server.

Microservices by business capability

flowchart TB
 UI[Marketplace client] --> O[Ordering]
 O --> I[Inventory]
 O --> P[Payments]
 O --> OD[(Order data)]
 I --> ID[(Stock data)]
 P --> PD[(Payment data)]

Ordering calls inventory and payments through contracts. Each service controls writes to its own data. Data ownership does not require a different database product for every service.

Shared tables make independent changes harder

flowchart LR
 O[Ordering] --> I[Inventory]
 I --> P[Payments]
 O --> DB[(Shared tables)]
 I --> DB
 P --> DB

Direct access to shared tables couples schemas and business rules. A synchronous chain adds waiting and dependency failures without guaranteeing autonomy.

Event-driven order updates

flowchart LR
 O[Ordering] -->|OrderConfirmed| B[Broker]
 B --> N[Notification consumer]
 B --> A[Analytics consumer]

Ordering publishes a fact. Each subscriber receives its own logical delivery and can process it later. Broker and subscription configuration determine actual delivery behavior.

Save the order and its message together

flowchart LR
 O[Ordering] --> T[Local transaction]
 T --> D[(Order and outbox rows)]
 D --> R[Relay]
 R --> B[Broker]
 B --> C[Consumer]

The relay publishes committed outbox records and retries failures. A crash after publication can cause a duplicate, so consumers still need safe repeat processing.

Order recovery across services

stateDiagram-v2
  direction LR
 [*] --> Pending
 Pending --> Reserved: stock reserved
 Reserved --> Confirmed: payment succeeds
 Reserved --> ReleasePending: payment declined
 ReleasePending --> Cancelled: stock released
 Reserved --> Reconciling: payment outcome unknown
 Reconciling --> Confirmed: charge found
 Reconciling --> ReleasePending: confirmed no charge

A decline can trigger stock release. An unknown payment outcome requires reconciliation before cancellation. Compensation is business work and can itself fail.

Translation at a legacy boundary

flowchart LR
 I[Inventory] -->|quantity in units| A[Supplier adapter]
 A -->|quantity in cases| S[Legacy supplier]

The adapter converts cases to units using an explicit pack size and maps supplier errors. A transport conversion alone does not resolve semantic differences.

Failure domains beyond services

flowchart TB
 subgraph H[One host]
 O[Ordering container]
 I[Inventory container]
 end
 O --> DB[(Shared database server)]
 I --> DB
 P[Payments on another host] --> DB

A host failure affects ordering and inventory. A shared database outage can also affect payments. Replicas in the same domain do not eliminate that shared risk.

A slow dependency causes a cascade

flowchart LR
 P[Payment slows] --> W[Checkout workers wait]
 W --> Q[Requests queue]
 Q --> T[Clients time out]
 T --> R[Retries add load]
 R --> W

Payment latency holds workers. New requests and retries increase contention until checkout becomes unavailable, including requests that might not need payment.

Examples, Discussions, and Project Ideas

Slide 2: Learning objectives

Architecture decisions connect business requirements to runtime behavior.

  • Distinguish layers, tiers, service boundaries, and failure domains.
  • Compare layered, microservice, and event-driven designs using one workflow.
  • Specify an integration contract and predict failure propagation.
  • Propose a team project with measurable failure experiments.

Slide 3: The campus marketplace

A fictional marketplace sells student-made goods for campus pickup.

  • A buyer reserves the final item, pays, and receives a pickup notification.
  • Stock must never fall below zero. A retry must not create a second charge.
  • Notifications may arrive later. Payment outcomes must remain visible.

Slide 4: One workflow, several boundaries

A boundary may separate code, deployment, ownership, or shared risk.

  • A layer groups code by responsibility. A tier groups runtime deployment.
  • A service owns a capability and exposes a contract.
  • A failure domain groups components vulnerable to the same fault.

Discuss: Can two containers still share one failure domain? Name the dependency that makes your answer true.

Slide 5: Clear responsibilities make checkout easier to change

Give request handling, business rules, and data storage separate jobs.

Reference: N-tier architecture

Slide 6: Two buyers want the last hoodie

Only one hoodie remains. Maya and Leo both click “Buy.”

  • Request handling checks that each buyer asked for one hoodie.
  • Business rules allow a purchase only while stock remains.
  • The database reserves the hoodie and changes stock from 1 to 0 together. The other buyer sees “Sold out.”

Slide 7: Layers organize code; tiers separate where it runs

Several code layers can run together inside one application tier.

Discuss: If we add another application instance, which layers are copied with it? Does the database gain capacity too?

Reference: N-tier architecture

Slide 8: One application with clearly separated parts

A modular monolith keeps catalog, ordering, and inventory organized within one deployment.

  • Catalog, ordering, and inventory expose internal interfaces.
  • A small team can use local calls and a shared transaction where appropriate.
  • One release and shared process resources couple modules operationally.

Slide 9: Discussion: the first architecture

Four developers have six weeks to deliver the marketplace.

  • Choose a modular monolith or independently deployed services.
  • Name one requirement that supports your choice.
  • Name evidence that would justify revisiting it.

Discuss: Would twenty services improve this team’s ability to deliver? Explain the coordination and testing cost.

Slide 11: Each service owns a business rule

Put each rule where one service can enforce it reliably.

  • Inventory prevents two buyers from reserving the last item.
  • Payments prevents a repeated request from charging twice and checks uncertain charge results.
  • Ordering tracks whether the purchase is waiting, confirmed, or cancelled.

Discuss: Why is one service per database table often a poor business boundary?

Slide 12: The cost of independent deployment

A service boundary creates work for development and operations.

  • Versioned contracts and consumer compatibility checks support separate releases.
  • Remote dependencies require deadlines, authentication, and tracing.
  • Cross-service changes need workflow recovery instead of assuming one local transaction.

Reference: Microservices architecture

Slide 13: Shared tables make independent changes harder

Separate services can still force teams to change and release together.

Discuss: Which boundary would you repair first: data ownership, a chatty API, or the deployment process? Explain your evidence.

Slide 15: Commands, events, and queries

Message names should expose the intended interaction.

  • ReserveStock is a command asking one owner to attempt a change.
  • StockReserved is an event recording a completed change.
  • GetReservation is a query that reads current state.

Discuss: Does an OrderPlaced event prove payment succeeded? What must the contract say?

Slide 16: The same event arrives twice

Order o42 is confirmed. A pickup-list worker receives event e17, then receives e17 again.

  • The event says what happened (OrderConfirmed), which order (o42), and which message (e17).
  • On the first delivery, save e17 and add o42 to the pickup list in one database transaction.
  • On a repeat delivery, find the saved e17 and skip the second pickup entry.

Discuss: What could go wrong if the worker saved e17 first, then crashed before adding the pickup entry?

Slide 17: Save the order and its message together

An outbox stores a message alongside the order so it can be sent after the database commits.

Reference: Transactional outbox

Slide 18: Order recovery across services

A workflow records intermediate states and compensating actions.

Slide 19: Discussion: an order arrives twice

Notification receives event e17, sends an email, and crashes before acknowledging.

  • Predict what happens when the broker redelivers e17.
  • Explain why acknowledging before the send risks losing the email.
  • Choose a policy for duplicate emails and a stricter policy for charges.

Discuss: Which side effect requires a provider-supported idempotency key? What will you do when that support is absent?

Slide 20: An agreement between ordering and payments

Example: ordering asks payments to charge $25 for order o42.

  • Meaning: send order o42, amount 2500 cents, currency USD, and request key pay-o42. Payments owns the charge result.
  • Access: only an authenticated caller with charge permission may make the request. Send a payment token, not card details.
  • Failure: after a 2-second deadline, show “Processing” and query pay-o42. Any retry uses the same key to avoid a second charge.

Slide 21: Translation at a legacy boundary

An adapter protects marketplace semantics from a supplier’s representation.

Discuss: If pack size changes, which owner detects the mismatch and prevents incorrect stock?

Reference: Anti-corruption layer

Slide 22: Failure domains beyond services

Shared infrastructure can fail several otherwise independent services.

Slide 24: Failure containment controls

Each control addresses a different mechanism.

  • Deadline: bounds waiting. Retry budget with jitter: limits additional attempts.
  • Circuit breaker: temporarily rejects calls to a failing dependency.
  • Bulkhead: reserves separate capacity so one dependency cannot consume everything.
  • Backpressure: slows admission when consumers cannot keep up.

Reference: Bulkhead pattern

Slide 25: Graceful degradation and correctness

A fallback must preserve the meaning of the user-visible result.

  • Recommendation failure can hide suggestions while catalog browsing continues.
  • Notification backlog can show “confirmed, notification pending.”
  • Uncertain payment must show “processing” and support reconciliation.

Discuss: Why is “payment succeeded” an unsafe fallback for a timeout?

Slide 26: A recommended design for the marketplace

Choose each interaction according to what the buyer needs before checkout can finish.

  • Keep request handling, business rules, and data access separate inside the application or each service.
  • Reserve stock through inventory before confirming a purchase. Give the call a deadline and a stable request key.
  • Confirm only after stock and payment outcomes are known. Keep uncertain payments in “Processing.”
  • After confirmation, publish an event through an outbox. Let notifications and analytics run separately.

Discuss: If notification stops for ten minutes, what should the buyer see after a successful purchase?

Slide 27: Architecture review workshop

Teams redesign the marketplace and test the design on paper.

  • Draw owners, data stores, synchronous edges, and events.
  • Trace one successful checkout and one unknown payment outcome.
  • Mark shared failure domains and choose a containment control.
  • Exchange designs and challenge one assumption.

Open the architecture workshop

Slide 28: Project problems: reliable buying and booking

Choose a user problem, then design a workflow that solves it.

  • Campus sellers lose trust when two buyers pay for the same last item. Help students buy available goods without duplicate charges or broken pickup promises.
  • Event attendees lose seats or face conflicting bookings during busy sales. Help organizers allocate each seat once and release abandoned holds.

Project scopes and experiments

Slide 29: Project problems: dependable stock and timely support

Start with the people affected and the outcome they need.

  • Small retailers receive inconsistent stock reports from suppliers. Help staff promise only validated stock and see when a supplier’s information is stale.
  • Community support teams struggle to sort incoming requests promptly. Help staff review suggested categories and responses while retaining approval of every action.

Project scopes and experiments

Slide 30: A reviewable project proposal

Explain the decision and the evidence that could change it.

  • Problem, actors, one workflow, and a business invariant.
  • Component diagram with data owners and integration contracts.
  • Architecture choice, rejected alternative, and shared failure risks.
  • Two failure experiments with expected outcomes and observable evidence.

Slide 31: Exit discussion

A defensible design explains both normal operation and recovery.

  • When can a layered application be the right choice?
  • What makes a service independently changeable?
  • What can duplicate delivery do to a business effect?
  • Which shared dependency determines your project’s failure domain?

Discuss: Name one boundary you would change in your team’s design and the tradeoff you accept.

Slide 32: References and next steps

Use the linked references to challenge your architecture decisions.

  • Required: Microsoft architecture guides for N-tier, microservices, and event-driven systems.
  • Required: Microsoft Bulkhead pattern.
  • Optional: Anti-corruption layer and AWS transactional outbox.
  • Module 5 develops queues, topics, delivery semantics, and broker implementation.

Open annotated references

Canvas remains the official source for project requirements, deadlines, submissions, and grading.

Key Takeaways

Lab Links