System Architecture
System architecture defines the high-level structure of software systems, including components, their relationships, and principles guiding their design and evolution.
What is System Architecture?
System architecture is the conceptual model that defines the structure, behavior, and views of a system. It serves as the blueprint for both system development and maintenance.
Architectural Patterns
Monolithic Architecture
┌─────────────────────────────┐
│ Monolithic App │
├─────────────────────────────┤
│ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │ UI │ │Logic│ │Data │ │
│ └─────┘ └─────┘ └─────┘ │
└─────────────────────────────┘
Characteristics:
- Single deployable unit
- Shared database
- Tight coupling
- Simple deployment
Pros:
- Easy development and testing
- Simple deployment
- No network latency
- Strong consistency
Cons:
- Difficult to scale components
- Technology lock-in
- Single point of failure
- Hard to maintain as it grows
When to Use:
- Small applications
- Simple business logic
- Limited team size
- Rapid prototyping
Microservices Architecture
┌──────┐ ┌──────┐ ┌──────┐
│Service│ │Service│ │Service│
│ A │ │ B │ │ C │
└───┬──┘ └───┬──┘ └───┬──┘
│ │ │
└──────────┼──────────┘
│
┌──────┴──────┐
│ API Gateway │
└──────┬──────┘
│
┌──────┴──────┐
│ Client │
└─────────────┘
Characteristics:
- Independent services
- Own databases
- Loose coupling
- API communication
Pros:
- Independent scaling
- Technology diversity
- Fault isolation
- Team autonomy
Cons:
- Network complexity
- Distributed transactions
- Operational overhead
- Testing complexity
When to Use:
- Large applications
- Multiple teams
- Different scalability needs
- Technology diversity
Layered Architecture
┌─────────────────┐
│ Presentation │ ← UI/Controllers
├─────────────────┤
│ Business Logic │ ← Services/Domain
├─────────────────┤
│ Data Access │ ← Repositories
├─────────────────┤
│ Database │ ← Storage
└─────────────────┘
Characteristics:
- Organized in horizontal layers
- Each layer communicates with adjacent layers
- Separation of concerns
Benefits:
- Clear separation of concerns
- Testability
- Maintainability
- Reusability
Architectural Quality Attributes
Performance
- Response Time: Time to process a request
- Throughput: Number of requests per second
- Latency: Delay in communication
- Resource Utilization: CPU, memory, network usage
Scalability
1# Vertical Scaling (Scale Up)
2def scale_vertically():
3 # Add more CPU, RAM, storage
4 increase_server_resources()
5
6# Horizontal Scaling (Scale Out)
7def scale_horizontally():
8 # Add more servers
9 add_more_servers()
10 load_balance_across_servers()Availability
- Uptime: System is operational
- Fault Tolerance: System continues despite failures
- Redundancy: Backup components
Security
- Authentication: Verify identity
- Authorization: Control access
- Encryption: Protect data
- Audit Logging: Track activities
Maintainability
- Modularity: Independent components
- Documentation: Clear architecture docs
- Testing: Comprehensive test coverage
- Code Quality: Clean, readable code
Design Principles
Separation of Concerns
1# Bad: Mixed responsibilities
2class UserHandler:
3 def save_user(self, user):
4 # Validation
5 if not user.email:
6 raise ValueError("Email required")
7
8 # Database operations
9 db.execute("INSERT INTO users...")
10
11 # Email sending
12 email_service.send_welcome(user.email)
13
14# Good: Separated concerns
15class UserValidator:
16 def validate(self, user):
17 if not user.email:
18 raise ValueError("Email required")
19
20class UserRepository:
21 def save(self, user):
22 db.execute("INSERT INTO users...")
23
24class EmailService:
25 def send_welcome(self, email):
26 # Send email logicSingle Responsibility Principle
Each component should have one reason to change.
Don't Repeat Yourself (DRY)
Avoid code duplication through abstraction.
Interface Segregation
Clients shouldn't depend on interfaces they don't use.
Dependency Inversion
Depend on abstractions, not concretions.
Architectural Decision Making
Trade-off Analysis
Consider:
- Performance vs. Complexity
- Consistency vs. Availability
- Security vs. Usability
- Cost vs. Features
Decision Framework
- Identify Requirements: Functional and non-functional
- Evaluate Options: Multiple architectural approaches
- Analyze Trade-offs: Pros and cons of each option
- Make Decision: Choose based on requirements
- Document: Record rationale and assumptions
Documentation
Document:
- Architectural decisions
- Rationale behind choices
- Assumptions and constraints
- Evolution over time
Common Architectural Patterns
Repository Pattern
1class UserRepository:
2 def __init__(self, db_connection):
3 self.db = db_connection
4
5 def find_by_id(self, user_id):
6 query = "SELECT * FROM users WHERE id = ?"
7 return self.db.execute(query, user_id)
8
9 def save(self, user):
10 query = "INSERT INTO users (name, email) VALUES (?, ?)"
11 self.db.execute(query, user.name, user.email)Factory Pattern
1class DatabaseFactory:
2 @staticmethod
3 def create_database(db_type):
4 if db_type == "mysql":
5 return MySQLDatabase()
6 elif db_type == "postgresql":
7 return PostgreSQLDatabase()
8 else:
9 raise ValueError(f"Unsupported database type: {db_type}")Observer Pattern
1class EventManager:
2 def __init__(self):
3 self.listeners = {}
4
5 def subscribe(self, event_type, listener):
6 if event_type not in self.listeners:
7 self.listeners[event_type] = []
8 self.listeners[event_type].append(listener)
9
10 def publish(self, event):
11 event_type = event.type
12 if event_type in self.listeners:
13 for listener in self.listeners[event_type]:
14 listener.handle(event)Architectural Evolution
Monolith to Microservices
- Identify Bounded Contexts: Natural business boundaries
- Extract Services: Move functionality to services
- Implement APIs: Communication between services
- Migrate Data: Separate databases
- Decommission: Remove old monolith code
Strangler Fig Pattern
Gradually replace legacy systems with new implementations:
- Build new functionality alongside existing system
- Redirect traffic to new system
- Decommission old components
- Repeat until fully migrated
Best Practices
- Start Simple: Begin with monolith, evolve as needed
- Measure Everything: Collect metrics and logs
- Design for Failure: Assume components will fail
- Automate Everything: Deployment, testing, monitoring
- Review Regularly: Architecture should evolve with requirements
- Document Decisions: Record architectural choices and rationale
- Consider Team Structure: Conway's Law - organization influences architecture
Common Pitfalls
- Over-engineering: Adding unnecessary complexity
- Premature Optimization: Optimizing before measuring
- Ignoring Non-functional Requirements: Focusing only on features
- Lack of Monitoring: No visibility into system behavior
- Technology Lock-in: Difficult to change technologies later
Good system architecture balances competing concerns while providing a foundation for growth and maintenance. It should be simple enough to understand but flexible enough to evolve.

