ARCHITECTURE IS A TRADEOFF
Choose the fit.
Understand the cost.
A CLI, a complex business application and independent services ask different things of their architecture. Change the scenario, compare the approaches, then read the lesson behind each choice.
These are editorial suitability ratings, not performance benchmarks, popularity statistics or a universal ranking. 1: usually avoid · 3: useful with a specific need · 5: strong starting choice. Styles can be combined.
17 approaches, sorted by suitability for small tool or cli.
Pipes and filters: data through independent stages
Compose transformations while making backpressure and errors explicit.
Simple monolith: start with one executable
Keep a small application understandable without premature service boundaries.
Layered architecture: transport, application, storage
Separate responsibilities without confusing logical layers with deployed tiers.
Microkernel: a small core with explicit extensions
Extend behavior through contracts without assuming dynamic shared libraries.
Modular monolith: boundaries inside one deployment
Give business capabilities explicit APIs while retaining local transactions.
Ports and adapters with a real request
Trace an HTTP adapter through an application operation to a storage port and compare test boundaries.
Vertical slices: organize around a use case
Keep feature changes together without duplicating shared invariants.
Clean and Onion: dependencies point toward policy
Keep domain policy independent of framework and database details.
Web, queue, worker: make slow work durable
Separate request latency from background completion.
BFF: an API shaped for one client
Compose client-specific views while retaining business ownership.
CQRS: different models for commands and queries
Separate change rules from read shape without assuming two databases.
DDD: bounded contexts and aggregates
Model language and invariants before choosing storage or transport.
Event sourcing: state from a durable history
Distinguish a source of truth from ordinary logs and notifications.
Event-driven services: delivery and idempotency
Treat duplicate delivery as normal and define the durable boundary.
Microservices: pay for independent ownership
Extract a remote boundary only when its independence is valuable.
Serverless handlers: bounded work and retry-safe effects
Separate invocation mechanics from deterministic application behavior.
SOA and RPC: stable service contracts
Compare service-oriented integration with smaller independently owned services.