Overview
Consensus leverages five key design patterns to achieve maintainability, extensibility, and testability. Each pattern solves a specific architectural challenge.Pattern Summary
| Pattern | Purpose | Location |
|---|---|---|
| Repository | Abstract data access, enable testing | src/repositories/ |
| Strategy | Support multiple voting algorithms | src/services/strategies/ |
| Observer | Track election events, enable audit logging | src/services/observers/ |
| Factory | Create ballots based on election type | src/services/factories/ |
| Adapter | Ensure vote anonymity | src/services/adapters/ |
1. Repository Pattern
Problem
Direct database access in business logic creates:- Tight coupling to database technology
- Difficult testing (requires real database)
- Mixed concerns (business logic + SQL)
Solution
Repository pattern abstracts data access behind interfaces:Implementation
Interface Definition:Benefits
- Database Independence: Swap SQLite for PostgreSQL by implementing new repositories
- Testability: Mock repositories for unit tests without database
- Single Responsibility: Repositories handle only data access
- Type Safety: Return domain entities, not raw database rows
Repository Instances
VoterRepository- Voter CRUD operationsElectionRepository- Election management, find active/closedCandidateRepository- Candidate CRUD, find by electionBallotRepository- Anonymous ballot storageVoterEligibilityRepository- Track voting statusVoteConfirmationRepository- Store vote receiptsAuditLogRepository- Immutable event logsAdminRepository- Admin user management
2. Strategy Pattern
Problem
Different election types require different vote counting algorithms:- FPTP - Simple plurality count
- AV - Ranked choice with elimination rounds
- STV - Proportional representation with quota calculation
Solution
Define an interface for voting strategies and implement each algorithm separately:Implementation
Strategy Interface:Benefits
- Open/Closed Principle: Add new voting systems without modifying existing code
- Single Responsibility: Each strategy handles one algorithm
- Testability: Test strategies in isolation
- Runtime Selection: Choose strategy based on election type
Available Strategies
- FPTPStrategy - First Past The Post (simple majority)
- AVStrategy - Alternative Vote (instant runoff)
- STVStrategy - Single Transferable Vote (proportional)
3. Observer Pattern
Problem
When elections change state (DRAFT→ACTIVE→CLOSED), multiple systems need notification:- Audit log must record the change
- Notifications might be sent to voters/admins
- Future: Email triggers, webhooks, etc.
ElectionService creates tight coupling.
Solution
Observer pattern allows objects to subscribe to election events:Implementation
Observer Interface:Benefits
- Loose Coupling:
ElectionServicedoesn’t know about audit logging - Extensibility: Add new observers without changing existing code
- Error Isolation: One failing observer doesn’t break others
- Runtime Configuration: Subscribe/unsubscribe observers dynamically
Current Observers
ElectionAuditLogger- Records state changes to audit logElectionNotifier- Placeholder for future notification system
4. Factory Pattern
Problem
Creating ballots requires complex logic:- FPTP elections need single-choice ballots
- STV/AV elections need ranked-choice ballots
- Each ballot needs unique ID and timestamp
- Input validation varies by election type
Solution
Centralize ballot creation logic in a factory:Benefits
- Centralized Logic: One place to handle ballot creation rules
- Type Safety: Ensures ballots are valid for their election type
- Consistent IDs: Guarantees unique identifiers
- Validation: Prevents invalid ballots from being created
5. Adapter Pattern
Problem
Critical Requirement: Votes must be anonymous - no linkage between voter and ballot. However, voters need confirmation they voted successfully. These two requirements seem contradictory.Solution
Adapter pattern transforms voter input into two separate outputs:- Anonymous Ballot - Contains vote choices, no voter ID
- Vote Confirmation - Contains voter ID, no vote choices
Implementation
Benefits
- Privacy Guarantee: Structural impossibility to link votes to voters
- Voter Assurance: Confirmation proves participation
- Double-Vote Prevention: Eligibility tracking without compromising anonymity
- Audit Trail: Separate audit logs without vote details
Pattern Interactions
Patterns work together to create a cohesive architecture:- Controller receives HTTP request
- VotingService orchestrates:
- Repository fetches election and voter
- Factory creates ballot
- Strategy validates ballot format
- Adapter separates ballot from confirmation
- Repository saves both separately
- Observer logs the event (if vote triggers election close)
Why These Patterns?
| Pattern | Chosen Because |
|---|---|
| Repository | Database abstraction is essential for testing and future scaling |
| Strategy | Multiple voting algorithms are core to the product |
| Observer | Audit logging is legally required; future integrations likely |
| Factory | Ballot creation rules are complex and type-dependent |
| Adapter | Vote anonymity is a hard requirement that needs architectural enforcement |
Anti-Patterns Avoided
- God Objects: Services are focused on single responsibilities
- Anemic Domain Model: Entities contain business logic, not just data
- Singleton Abuse: Only used for database connection (justified)
- Magic Strings: Enums for all status values
- Tight Coupling: Dependency injection throughout
Next Steps
- Data Layer - Repository implementations and database schema