KaroKar Backend is architected as a domain-driven modular monolith: a single deployable NestJS application whose internals are partitioned into eight self-contained bounded contexts, each owning its own entities, services, repositories, and business rules. This approach was chosen deliberately over microservices — the engineering team is small, the business is growing fast, and distributing services too early would introduce distributed transaction complexity and operational overhead that the current scale does not justify. The modular structure means any domain can be extracted into an independent service later, without redesigning existing entities or breaking other domains.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/Codefied-CodePix/Karokar-backend/llms.txt
Use this file to discover all available pages before exploring further.
Why a Modular Monolith? (ADR-001)
The decision to adopt a modular monolith was driven by the need for rapid delivery without sacrificing future architectural flexibility. The core trade-offs: Microservices were rejected because they introduce:- Distributed transactions and two-phase commit complexity
- Network latency between every domain call
- Service discovery and infrastructure overhead
- Substantially higher operational costs at small scale
- Faster development and easier onboarding for a small team
- Simple single-unit deployments with no orchestration layer
- Strong domain boundaries enforced at the module level
- A clear future extraction path — domains can become microservices as scale demands
- Lower infrastructure costs for MVP and early growth phases
The architecture’s success criterion: new domains (approvals, KYC, maintenance, AI recommendations, dynamic pricing) can be added without modifying existing domains, and the system can scale to multiple business models while maintaining clear domain ownership.
The Eight Bounded Contexts
Each domain is a self-contained NestJS module registered inAppModule. Cross-domain communication happens exclusively through domain events — no domain imports a service or repository from another.
Identity
Users, auth, organization membership.
Manages users, roles, authentication flows, JWT issuance, permission resolution, and organization structures. Multi-tenancy is anchored here: every other domain carries the
organizationId originating from this context.Fleet
Vehicles, availability, maintenance, suspension.
Owns vehicle records, document attachments, real-time availability windows, vehicle status transitions, and suspension workflows. Fleet state is changed only through Fleet domain services.
Booking
Booking lifecycle and state machine.
Handles booking requests, drives the full booking state machine (pending → confirmed → active → completed / cancelled), and manages reservation periods. State transitions are owned exclusively by Booking domain services.
Assignment
Employee-vehicle assignment lifecycle.
Models corporate employee-to-vehicle allocation and the multi-step handover lifecycle for long-term vehicle assignments, distinct from short-term bookings.
Verification
Generic verification framework for any entity.
Provides a reusable verification request / status-tracking mechanism applicable to users, vehicles, or organizations. Designed with hooks for future NADRA and government API integrations.
Notification
Event-driven notifications (email, SMS, push).
Listens to domain events emitted by all other contexts and dispatches notifications across email, SMS, and push channels. Has zero awareness of business logic — it only reacts to events.
Audit
Compliance records from domain events.
Subscribes to domain events across all contexts and writes immutable audit / change-history records. Business domains must never call audit services directly — all audit records arise from events.
Administration
Vendor and corporate approval workflows.
Manages vendor onboarding approval, corporate organization approval, and platform-wide moderation. Surfaces administration actions to Platform Administrators.
Module File Structure
Every domain module follows the same layout, enforced by the NestJS conventions rule (01-nestjs-conventions.mdc):
Naming Conventions
| File type | Convention | Example |
|---|---|---|
| Entity | <domain>.entity.ts | booking.entity.ts |
| Service | <domain>.service.ts | booking.service.ts |
| Repository | <domain>.repository.ts | booking.repository.ts |
| DTO | create-<domain>.dto.ts | create-booking.dto.ts |
| Event | <domain>-<past-tense>.event.ts | booking-approved.event.ts |
| Controller | <domain>.controller.ts | booking.controller.ts |
| Module | <domain>.module.ts | booking.module.ts |
Entity Baseline
All entities extend a sharedBaseEntity that provides:
id—@PrimaryGeneratedColumn('uuid')(UUID v4)createdAt— auto-set timestampupdatedAt— auto-updated timestamporganizationId— tenant isolation anchor (required on every entity)
@Column({ name: 'column_name' }).
Non-Negotiable Architectural Rules
These rules are enforced at code-review time and captured in00-architecture.mdc. Violations are considered blocking defects.
- Domains MUST NOT import services or repositories from other domains directly. Any cross-domain data need must be satisfied by domain events or a read-side query within the consuming domain’s own infrastructure layer.
-
Cross-domain communication MUST happen through domain events only.
Use
@nestjs/event-emitterwith wildcard event names (e.g.booking.confirmed,fleet.vehicle.suspended). The EventEmitter is configured withwildcard: trueand delimiter.. -
Every entity MUST carry an
organizationIdfor tenant isolation. No entity may exist without a link to its owning organization. This is the enforcement point for multi-tenancy. - Business state transitions MUST only occur through domain services. Controllers, scheduled jobs, and event handlers must never mutate business state directly. They delegate to the appropriate domain service.
-
Authorization MUST be permission-based.
Guards check granular permission strings such as
booking.createoremployee.manage. Checkinguser.role === 'CorporateAdmin'directly is prohibited. - Audit records MUST be generated by the Audit Domain subscribing to events. Business domains must never call audit services directly. The Audit domain is the single consumer responsible for compliance records.
Domain Event Bus
The current event infrastructure uses@nestjs/event-emitter running in-process:
| Phase | Mechanism | Notes |
|---|---|---|
| Current | In-process EventEmitter | Zero infrastructure, synchronous |
| Next | Transactional Outbox pattern | Events persisted atomically with business data |
| Future | Kafka or AWS SNS/SQS | Enables independent service extraction |
Because the EventEmitter is in-process, there is no message broker to operate during local development. Event subscribers in the Notification and Audit domains react synchronously within the same request lifecycle. When the Outbox pattern is introduced, this behaviour will change — event handlers will execute asynchronously after the transaction commits.
See Also
- Introduction — participant model and tech stack overview
- Quickstart — run the server locally in under five minutes