An honest retrospective on the most challenging design decisions we faced and the key lessons learned about software architecture practice.
Two decisions that required the deepest architectural reasoning and the most debate.
The most difficult architectural decision was determining the exact boundary between Abokabot and the Menged external system. Initially, the team considered having each service communicate with Menged independently, which would have reduced coupling within our system but created uncontrolled external API usage and multiple places to handle Menged authentication tokens. After extensive discussion, we settled on the Ticketing Service as the sole Menged communicator, with an anti-corruption layer (the menged-adapter) that translates Menged's API contract into our internal domain types. This required carefully thinking through the Dependency Inversion Principle: services depend on a CardValidationPort interface, not the concrete adapter. This decision proved correct when we simulated a Menged API version upgrade and realized only one file needed changing.
Fare deductions must be atomic to prevent double charges, demanding strong consistency (CP in CAP theorem). But boarding terminals also need to function during transient network issues, demanding availability. These goals are in direct tension. The solution was a tiered consistency model: online validation uses full two-phase commit with Menged; offline mode issues a single-journey pre-authorized token with strict replay protection. Designing this required deep engagement with distributed systems concepts—specifically how to design for partial failure. The realization that perfect consistency and perfect availability cannot coexist in a distributed system was a pivotal moment that shaped every subsequent technical decision.
What this assignment taught us about the discipline of software architecture.
Every architectural decision involves trading one quality attribute for another. Choosing strong consistency for fare deductions reduces availability during network partitions. Choosing caching for performance introduces eventual consistency windows. The 4+1 view model helped us make these trade-offs explicit and ensure every stakeholder understood the implications.
The Menged integration shaped more of our architecture than any internal decision. External system reliability, API latency, and authentication patterns rippled into our caching strategy, circuit-breaker design, and offline mode. We learned to treat external systems as unreliable and design defensively from the start rather than assuming happy-path integration.
Before this assignment, SOLID principles and design patterns felt like abstract academic concepts. Applying them to a real system exposed their practical value. The Observer pattern genuinely solved a messy coupling problem in our event handling. The Singleton saved us from distributed configuration bugs. Understanding the "problem each pattern solves" is far more valuable than memorizing their structures.
Writing this website forced precision in our architectural thinking. Vague ideas that felt clear in discussion became obviously underspecified when written down. The process of creating Mermaid diagrams for the sequence flow revealed an edge case in the offline boarding scenario that we had completely overlooked in verbal discussions. Clear documentation is not documentation of the architecture—it is part of the architecture.
This documentation represents the collective effort of the Abokabot team at Bahir Dar University. Every section has been developed with academic rigor and a genuine attempt to apply software architecture principles to a real-world problem relevant to our city. We hope Abokabot can one day contribute to transforming Bahir Dar's public transport experience.