When someone says, “We need a new platform,” the first question I ask is not Which framework should we use? It is: What problem are we solving, for whom, and what should change after we solve it?
That simple shift changes how I approach solution architecture. Architecture is not a collection of technologies. It is a set of decisions that connects a business problem to a system that can actually be built, operated, and changed.
1. Understand the Problem Before Designing the System
I normally clarify five things first:
- Who are the users and what workflow are they trying to complete?
- What is the expected business outcome?
- What constraints already exist?
- What volume, latency, security, and availability expectations matter?
- Which parts are fixed and which can evolve?
For example, “build an order platform” is too broad. A better starting point is: customers create orders, inventory must be reserved, payment must be confirmed, and the customer must receive a reliable status update even when a downstream service fails.
2. Define Responsibilities and Boundaries
Once the workflow is clear, I split responsibilities based on business capability rather than randomly creating services.
A simple flow might look like:
Client
↓
Order API
↓
Order Service ──→ PostgreSQL
↓
Queue
├── Inventory Worker
├── Payment Worker
└── Notification Worker
Each component has a clear responsibility. That makes ownership, scaling, testing, and failure handling much easier.
3. Design APIs Around Business Actions
Good APIs expose useful business operations instead of leaking internal database structure.
POST /orders
GET /orders/:id
POST /orders/:id/cancel
For asynchronous operations, the API should also make the state transition clear. For example, creating an order can return processing while workers complete inventory and payment steps.
4. Think About Orchestration and Failure Paths
The happy path is easy. Production architecture is mostly about what happens when something goes wrong.
If payment succeeds but the notification service fails, should the order be rolled back? Usually not. The architecture needs a deliberate policy, such as durable events, retries, idempotency, and a reconciliation process.
For example, a worker should be safe to retry:
if alreadyProcessed(eventId):
return
processPayment(event)
markProcessed(eventId)
The exact implementation varies, but the principle is important: retries must not accidentally create duplicate business actions.
5. Choose Technology Based on Constraints
I don't start with “Node.js everywhere” or “microservices everywhere.” I look at the team, traffic, data model, operational maturity, delivery timeline, and expected change.
A modular monolith can be the right starting point. A queue may be more useful than another microservice. PostgreSQL may be enough where a collection of specialized databases would only add operational complexity.
6. Architecture Should Become a Delivery Plan
A good architecture document should answer what the team builds first. I usually turn the design into milestones such as:
- Define domains, contracts, security, and infrastructure foundations.
- Implement the critical business workflow.
- Add asynchronous processing and external integrations.
- Add observability, resilience, and operational tooling.
- Measure production behaviour and optimize based on evidence.
My Practical Takeaway
Solution architecture is about reducing uncertainty. Start with the problem, make responsibilities explicit, design the important failure paths, choose technology based on constraints, and connect the architecture to a realistic delivery plan.
The best architecture is not necessarily the most complex one. It is the one the team can understand, operate, and evolve while still solving the actual business problem.