Problem & Context
What Needed to Change.
Indonesian pharmacies and clinics need to coordinate sales, expiring medicine stock, prescriptions, patient visits, clinical records, billing, payments, and BPJS administration without losing tenant, branch, or staff attribution across handoffs.
The platform supports multi-branch pharmacy and clinic operators, with workflows for cashiers, pharmacists, inventory staff, receptionists, doctors, nurses, finance staff, tenant administrators, and platform administrators.
Constraints & Tradeoffs
The Shape of the Decision Space.
Constraints
- Every tenant-owned operation must preserve tenant scope, and branch-operational writes require an explicit branch context.
- Rupiah amounts are stored and calculated as integers, while inventory quantities must never become negative.
- Clinical and identity data require permission separation, PII-safe logs, redacted audit payloads, and attributable changes.
- BPJS communication is an external, failure-prone dependency, so clinical completion cannot depend on immediate gateway availability.
Tradeoffs
- A modular monolith keeps deployment and cross-domain transactions simple, but modules cannot be scaled or released independently like services.
- Tenant and branch predicates are written explicitly in queries for clarity and control, at the cost of repetitive code and no automatic type-level enforcement.
- Redis-backed jobs are optional and disabled by default, reducing local infrastructure requirements while leaving scheduled BPJS sending unavailable until enabled.
- Runtime feature access is driven by tenant module records instead of tenant type, enabling flexible product bundles but adding provisioning and route-gating complexity.
Architecture
A Deliberately Legible System.
- 01
Web Application
Next.js App Router pages render Indonesian-language workflows and compose authentication, branch selection, internationalization, notifications, and TanStack Query state.
- 02
Scoped API Client
The browser client attaches the access token and selected branch, refreshes expired sessions, and clears cached data when the active branch changes.
- 03
Fastify Transport
NestJS runs on Fastify with versioned API routes, request IDs, CORS, multipart limits, API documentation, and structured exception handling.
- 04
Authorization and Validation
Controllers combine authentication, tenant, branch, module, and permission guards with Zod validation before invoking domain services.
- 05
Domain Transactions
Services coordinate sales, FEFO stock consumption, clinic state transitions, clinical finalization, billing, payments, and audit records inside scoped transactions.
- 06
PostgreSQL Persistence
Drizzle maps tenant- and branch-aware tables for integer money values, inventory batches, immutable stock movements, clinical records, invoices, payments, and BPJS submission state.
- 07
Background Integration
When enabled, BullMQ and Redis provide retryable jobs and scheduled BPJS processing while restoring tenant and branch context for each operation.
Screens & States
The Interface in Context.


Results
What the System Delivers.
- Pharmacy sales with draft autosave, payments, prescription checkout, returns, and branch-scoped sale numbers.
- FEFO batch consumption, unit conversion, guarded stock updates, stock opname, transfers, and append-only stock movement records.
- A clinic workflow connecting reception, queues, clinical documentation, finalization, invoicing, and payment attribution.
- BPJS validation, dependency ordering, retry handling, duplicate protection, correction notes, and optional scheduled delivery.
Lessons
What I Would Carry Forward.
- Multi-tenancy requires explicit tenant and branch predicates on every read and write, including mutation conditions.
- Inventory correctness needs a single transaction covering FEFO selection, guarded quantity updates, payment state, and a durable movement ledger.
- External healthcare integrations are safer when deterministic payload construction is separated from retryable network delivery and duplicate protection.
- Shared validation schemas and focused property tests strengthen state machines, role boundaries, date handling, and cross-package request contracts.