Chapter 24: Exercises
Tier 1 -- Recall and Comprehension (Exercises 1-6)
These exercises test your ability to recall key concepts and demonstrate understanding of fundamental architectural ideas.
Exercise 1: Architecture Vocabulary Matching
Match each term in Column A with its correct definition in Column B.
| Column A | Column B |
|---|---|
| 1. Monolith | A. A decision that is expensive to change later |
| 2. Microservices | B. Clients should not depend on interfaces they do not use |
| 3. Architectural decision | C. A single deployable unit containing all application functionality |
| 4. SOLID | D. High-level modules should not depend on low-level modules |
| 5. Interface Segregation | E. An architecture where the application is decomposed into small, independently deployable services |
| 6. Dependency Inversion | F. Five object-oriented design principles: Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion |
Exercise 2: Pattern Identification
For each of the following scenarios, identify whether the described system is using a monolithic, microservices, or serverless architecture. Explain your reasoning in one to two sentences.
-
A team deploys their entire application as a single Docker container. All features share one PostgreSQL database. When they need to update the payment logic, they redeploy the whole application.
-
A company runs 12 separate services, each with its own database. Services communicate via REST APIs and a message queue. Each service is deployed independently by the team that owns it.
-
A startup's application consists of 30 AWS Lambda functions triggered by API Gateway endpoints, S3 events, and scheduled CloudWatch rules. They pay only when functions execute.
-
A small team runs a Django application on a single server. They recently split the background job processing into a separate Celery worker, but both the web app and the worker share the same codebase and database.
Exercise 3: SOLID Principle Identification
Read each code snippet and identify which SOLID principle it violates. Explain why.
Snippet A:
class UserService:
def create_user(self, name, email):
# Create user in database
user = {"name": name, "email": email}
self.save_to_db(user)
# Send welcome email
self.send_email(email, "Welcome!", "Thanks for joining.")
# Log analytics event
self.track_event("user_created", {"name": name})
return user
Snippet B:
class Bird:
def fly(self):
return "Flying high!"
class Penguin(Bird):
def fly(self):
raise NotImplementedError("Penguins can't fly!")
Snippet C:
class ReportGenerator:
def generate(self, data, format_type):
if format_type == "pdf":
return self._generate_pdf(data)
elif format_type == "csv":
return self._generate_csv(data)
elif format_type == "html":
return self._generate_html(data)
# Adding a new format requires modifying this class
Exercise 4: Trade-Off Pairs
For each of the following architectural decisions, identify the primary trade-off. What do you gain, and what do you give up?
- Switching from a relational database (PostgreSQL) to a document database (MongoDB) for a social media application.
- Adding a Redis caching layer in front of your database.
- Splitting a monolith into microservices.
- Moving from self-hosted servers to a serverless architecture.
- Implementing event sourcing instead of traditional CRUD operations.
Exercise 5: ADR Comprehension
Read the following ADR and answer the questions below.
# ADR-003: Use Redis for Session Storage
## Status
Accepted
## Context
Our web application currently stores session data in the application's
memory. This works for a single server but prevents us from scaling
horizontally because sessions are lost when a user's request is routed
to a different server.
## Decision
We will use Redis as an external session store.
## Alternatives Considered
- Sticky sessions (route users to the same server)
- Store sessions in PostgreSQL
- JWT tokens (stateless sessions)
## Consequences
- Redis becomes a new infrastructure dependency
- Session data survives server restarts
- We can now scale horizontally behind a load balancer
- Redis requires monitoring and backup
Questions: 1. What problem prompted this decision? 2. Why was "sticky sessions" likely rejected? (Infer from context.) 3. What new operational burden does this decision introduce? 4. Is this a Type 1 (irreversible) or Type 2 (reversible) decision? Why?
Exercise 6: Event-Driven Concepts
Explain the difference between: 1. A domain event and a system event, with one example of each. 2. The publish-subscribe pattern and direct function calls, in terms of coupling. 3. Event sourcing and traditional CRUD storage, in terms of data stored.
Tier 2 -- Application (Exercises 7-12)
These exercises require you to apply concepts from the chapter to practical scenarios.
Exercise 7: Design a Module Structure
You are building a library management system with the following features: - Book catalog (add, search, browse books) - Member management (register, update, deactivate members) - Borrowing and returns (check out books, return books, track overdue items) - Notifications (send reminders for overdue books, new book alerts)
Design a module structure with clean boundaries. For each module: 1. Name the module 2. List the data it owns 3. Define the public interface (method signatures) 4. Identify its dependencies on other modules
Exercise 8: Refactor to SRP
Refactor the following class so that each resulting class has a single responsibility:
class OrderProcessor:
def __init__(self):
self.orders = []
def create_order(self, customer, items):
order = {"customer": customer, "items": items, "total": 0}
for item in items:
order["total"] += item["price"] * item["quantity"]
if order["total"] > 100:
order["total"] *= 0.9 # 10% discount
self.orders.append(order)
self.save_to_database(order)
self.send_confirmation_email(customer, order)
self.update_inventory(items)
return order
def save_to_database(self, order):
print(f"Saving order to database: {order}")
def send_confirmation_email(self, customer, order):
print(f"Sending email to {customer}: Order total ${order['total']}")
def update_inventory(self, items):
for item in items:
print(f"Reducing stock for {item['name']} by {item['quantity']}")
Exercise 9: Implement Dependency Inversion
You have a weather reporting service that directly calls an external API:
import requests
class WeatherReport:
def get_current_weather(self, city):
response = requests.get(
f"https://api.weather.com/current?city={city}"
)
data = response.json()
return f"{city}: {data['temp']}°C, {data['condition']}"
Refactor this class to use dependency inversion:
1. Define an abstract WeatherProvider interface
2. Create a concrete ApiWeatherProvider that calls the real API
3. Create a MockWeatherProvider for testing
4. Modify WeatherReport to accept a WeatherProvider via constructor injection
5. Show how to instantiate WeatherReport for both production and testing
Exercise 10: Event Bus Implementation
Extend the EventBus from code/example-02-event-driven.py to support:
1. Event filtering: Allow subscribers to provide a filter function that determines whether they should receive a specific event (e.g., only receive "task.completed" events where the assignee is "alice").
2. Priority ordering: Allow handlers to specify a priority (integer), and execute higher-priority handlers first.
3. Write a demonstration that shows both features working.
Exercise 11: Caching Strategy
You have a product catalog service with these characteristics: - 10,000 products in the database - Products are updated approximately 5 times per day - Product pages are viewed 100,000 times per day - Price changes must be reflected within 5 minutes
Design and implement a caching strategy:
1. Choose a caching pattern (cache-aside, write-through, or write-behind) and justify your choice.
2. Implement a CachedProductRepository that wraps a ProductRepository.
3. Handle cache invalidation appropriately.
4. Write tests that verify cache hits, cache misses, and cache invalidation.
Exercise 12: AI Architecture Prompt
Write a detailed RADIO-formatted prompt (Requirements, Actors, Data, Interfaces, Operational constraints) for the following project:
A real-time collaborative document editor (like Google Docs) for a team of 200 people. The system should support: - Simultaneous editing by up to 20 users on the same document - Version history with the ability to revert to any previous version - Comments and suggestions - Offline editing with sync when back online
Include at least 3 specific questions you would ask the AI as follow-up after receiving its initial architecture proposal.
Tier 3 -- Analysis (Exercises 13-18)
These exercises require you to break down systems, compare approaches, and identify strengths and weaknesses.
Exercise 13: Architecture Review
Review the following proposed architecture for an e-commerce platform and identify at least five concerns or potential issues:
Architecture: Microservices on Kubernetes
Services:
- User Service (Node.js + MongoDB)
- Product Service (Python + PostgreSQL)
- Order Service (Java + MySQL)
- Payment Service (Go + PostgreSQL)
- Notification Service (Python + Redis)
- Search Service (Python + Elasticsearch)
- Analytics Service (Python + ClickHouse)
- Recommendation Service (Python + TensorFlow + Redis)
Communication: All services communicate via synchronous REST APIs.
Deployment: Each service runs in its own Kubernetes pod.
Team: 4 developers, 1 DevOps engineer.
Timeline: 6 months to MVP.
For each concern, explain why it is problematic and suggest an improvement.
Exercise 14: Monolith vs. Microservices Decision
A healthcare startup is building a patient management system. Analyze whether they should use a monolith or microservices given these constraints:
- Team: 6 developers (2 senior, 4 junior)
- Timeline: 4 months to first release
- Users: 500 healthcare providers in Year 1, potentially 10,000 in Year 3
- Data: Patient records (highly sensitive, HIPAA-regulated)
- Features: Appointment scheduling, patient records, billing, lab results, messaging
- Budget: $3,000/month for infrastructure
Write a structured analysis covering: 1. Recommendation with justification 2. How to structure the chosen architecture 3. What would trigger a migration to the other pattern 4. Key risks and mitigations
Exercise 15: SOLID Violation Audit
Audit the following codebase module and identify all SOLID violations. For each violation, state which principle is broken and provide corrected code.
class DatabaseManager:
"""Manages all database operations for the application."""
def __init__(self, connection_string):
self.connection = self.connect(connection_string)
def connect(self, connection_string):
return f"Connected to {connection_string}"
def save_user(self, user):
print(f"INSERT INTO users: {user}")
def save_order(self, order):
print(f"INSERT INTO orders: {order}")
def save_product(self, product):
print(f"INSERT INTO products: {product}")
def send_email(self, to, subject, body):
print(f"Email to {to}: {subject}")
def generate_report(self, report_type):
if report_type == "sales":
return "Sales report data"
elif report_type == "users":
return "Users report data"
elif report_type == "inventory":
return "Inventory report data"
def validate_user(self, user):
if not user.get("email"):
raise ValueError("Email required")
if not user.get("name"):
raise ValueError("Name required")
Exercise 16: Event-Driven Analysis
An online marketplace uses event-driven architecture. The following events flow through the system when a customer makes a purchase:
OrderPlaced--> triggersPaymentService,InventoryService,NotificationServicePaymentProcessed--> triggersOrderService(update status),NotificationServicePaymentFailed--> triggersOrderService(cancel),InventoryService(release),NotificationServiceInventoryReserved--> triggersShippingServiceShipmentCreated--> triggersNotificationService,TrackingService
Analyze this event flow:
1. What happens if the InventoryService is down when OrderPlaced fires?
2. What happens if PaymentProcessed and InventoryReserved events arrive out of order at the ShippingService?
3. How would you handle the scenario where payment succeeds but inventory is out of stock?
4. What monitoring would you need to ensure this system is healthy?
Exercise 17: Scalability Bottleneck Analysis
A social media application has the following read/write patterns:
- 1 million daily active users
- Average user views 50 posts per session (READ)
- Average user creates 2 posts per day (WRITE)
- Average user likes 10 posts per day (WRITE)
- The "timeline" (feed) for each user is generated dynamically by querying posts from followed users
Current architecture: - Single PostgreSQL database - Python Flask application on 4 load-balanced servers - No caching
Identify the top 3 scalability bottlenecks and propose solutions for each. For each solution, describe: 1. What it solves 2. What complexity it introduces 3. When you would implement it (user count threshold)
Exercise 18: Dependency Graph Analysis
Draw (or describe textually) the dependency graph for the following modules and identify: 1. Which modules are most tightly coupled? 2. Which module, if changed, would cause the most cascading changes? 3. Where would you introduce abstractions to reduce coupling?
Module A (UserController) imports: UserService, UserValidator, EmailService, Logger
Module B (UserService) imports: UserRepository, EmailService, Logger, CacheService
Module C (UserRepository) imports: DatabaseConnection, Logger
Module D (EmailService) imports: SMTPClient, TemplateEngine, Logger
Module E (UserValidator) imports: UserRepository, Logger
Module F (CacheService) imports: RedisClient, Logger
Tier 4 -- Evaluation (Exercises 19-24)
These exercises require you to make and justify judgments about architectural choices.
Exercise 19: Architecture Decision Record
Write a complete ADR for the following scenario:
Your team is building a real-time notification system. You need to choose between: - Option A: WebSockets for real-time push notifications - Option B: Server-Sent Events (SSE) for one-way real-time updates - Option C: Long polling as a simpler fallback approach
Context: The system needs to push notifications to users in real time (task assignments, comments, status changes). The team has experience with REST APIs but not with WebSockets. The application must work behind corporate proxies. Expected load is 5,000 concurrent connections.
Write a full ADR with Status, Context, Decision, Alternatives Considered (with pros/cons), and Consequences.
Exercise 20: Architecture Critique
A colleague presents the following architecture for a new project management tool:
"We will build the entire application as serverless functions on AWS Lambda. Each API endpoint will be its own Lambda function. We will use DynamoDB for storage because it scales automatically. All business logic will live in the Lambda functions. We will use API Gateway to route requests. For real-time updates, we will use WebSocket connections through API Gateway."
Write a detailed critique of this architecture covering: 1. What this architecture does well 2. What risks or challenges it introduces 3. What assumptions it makes that might be wrong 4. What alternative approaches would you suggest for the riskiest aspects
Exercise 21: Technology Trade-Off Evaluation
Your team needs to choose a message broker for an event-driven architecture. The candidates are: - RabbitMQ - Apache Kafka - AWS SQS - Redis Streams
Create a weighted decision matrix with at least 6 criteria relevant to your team (a 5-person startup building a SaaS analytics platform processing 100,000 events per day). Score each option and provide a recommendation with justification.
Exercise 22: Scalability Strategy Evaluation
You are the lead developer of an application that currently serves 10,000 users and is growing 50% month-over-month. Your CEO asks you to prepare the application for 1 million users within 18 months.
Evaluate the following proposed scaling roadmap and identify what is missing, what is in the wrong order, and what is unnecessary:
- Month 1-3: Migrate to Kubernetes with auto-scaling
- Month 3-6: Split into microservices (user, content, analytics)
- Month 6-9: Implement sharded database architecture
- Month 9-12: Add global CDN and edge computing
- Month 12-15: Implement CQRS and event sourcing
- Month 15-18: Add machine learning-based auto-scaling
Exercise 23: SOLID vs. Pragmatism
For each of the following scenarios, decide whether strict adherence to SOLID principles is appropriate or whether a pragmatic violation is justified. Explain your reasoning.
- A weekend hackathon project that might become a real product.
- A data migration script that will run once and then be deleted.
- The core domain model of a financial services application.
- A prototype being built to validate a business hypothesis with 10 beta users.
- A shared library used by 15 different teams across the organization.
Exercise 24: Conway's Law Assessment
Your organization has the following team structure: - Team A: Frontend (4 developers) - Team B: Backend API (3 developers) - Team C: Data/ML (2 developers) - Team D: DevOps (1 developer)
According to Conway's Law, the system architecture will tend to mirror this team structure.
- Predict the most likely architecture that emerges from this team structure.
- Identify the communication bottlenecks this structure creates.
- If you could reorganize the teams (same 10 people), what structure would better support a microservices architecture?
- What structure would better support a monolithic architecture?
Tier 5 -- Synthesis and Creation (Exercises 25-30)
These exercises require you to create original designs, implementations, and documentation.
Exercise 25: Design a Complete Architecture
Design the architecture for an online learning platform (like a simplified Coursera) with these requirements: - Instructors can create courses with video lessons and quizzes - Students can enroll, watch videos, take quizzes, and earn certificates - Support for 50,000 students and 500 instructors - Video streaming with adaptive quality - Real-time quiz grading - Discussion forums per course - Progress tracking and analytics
Produce: 1. A system context diagram (described in text) 2. A container diagram (major components and their technologies) 3. Module structure for the main backend service 4. Data flow diagram for the "student takes a quiz" use case 5. An ADR for your most significant architectural decision
Exercise 26: Build an Event-Sourced Aggregate
Implement an event-sourced ShoppingCart aggregate with the following capabilities:
- Add items (product ID, quantity, unit price)
- Remove items
- Change item quantity
- Apply discount codes (percentage or fixed amount)
- Calculate total
- Checkout (produces an OrderPlaced event)
Requirements:
- All state changes are recorded as events
- Current state is derived by replaying events
- Implement a get_cart_at(timestamp) method for temporal queries
- Include a to_order_summary() method that produces a final summary
- Write tests that verify event replay produces correct state
Exercise 27: Implement a Plugin Architecture
Build a plugin-based text processing system that demonstrates OCP:
- Define a TextPlugin interface with a process(text: str) -> str method
- Create a TextPipeline class that chains plugins together
- Implement at least 5 plugins: UppercasePlugin, TrimPlugin, CensorPlugin (replaces specified words), MarkdownToHtmlPlugin, and WordCountPlugin
- Users should be able to add custom plugins without modifying any existing code
- Include a plugin registry that can discover and load plugins by name
Exercise 28: Architecture Migration Plan
You have a monolithic Django application with the following structure: - User management (authentication, profiles, permissions) - Product catalog (products, categories, search) - Order processing (cart, checkout, payment) - Inventory management (stock levels, reordering) - Reporting (sales reports, user analytics)
The application has grown to 100,000 lines of code, and the 15-person team is experiencing merge conflicts and slow deployments.
Create a detailed migration plan to extract the first two microservices: 1. Which services would you extract first, and why? 2. What would the interface between the monolith and the new services look like? 3. How would you handle data that is currently shared across modules? 4. What is the rollback plan if the migration fails? 5. How would you test the migration without affecting production users? 6. Provide a timeline with milestones
Exercise 29: Full-Stack Architecture with AI
Using an AI assistant (or simulating the conversation), design the complete architecture for a project management tool that competes with Trello. Document the entire design conversation:
- Write the initial RADIO prompt
- Record the AI's response (or generate a realistic one)
- Write 3 follow-up prompts that challenge or refine the proposal
- Produce the final architecture document including: - System overview - Component descriptions - Data model - API design (key endpoints) - Technology choices with justifications - Scalability plan - 3 ADRs for the most important decisions
Exercise 30: Architecture Quality Attributes Framework
Create a reusable "Architecture Quality Assessment Checklist" that a team can use to evaluate any architecture proposal. The checklist should:
- Cover at least 8 quality attributes (e.g., performance, security, maintainability, scalability, reliability, observability, cost-efficiency, developer experience)
- For each attribute, provide 3-5 specific, actionable questions to ask
- Include a scoring rubric (how to rate each attribute 1-5)
- Include a section on "red flags" -- warning signs that indicate serious problems
- Include guidance on which attributes matter most for different project types (startup MVP, enterprise application, high-traffic consumer app)
- Write an example evaluation applying this checklist to a simple architecture proposal