The application layer

The domain model gives you the nouns and the facts — aggregates, value objects, the events they emit. The application layer gives you the verbs: the operations a caller actually invokes to act on that domain. You have modeled what an Order is; now you model what a caller can do — place one, pay it, list a customer's orders, react when one is placed.

Vishwakarma splits those verbs into four constructs, plus one rule that guards them:

ConstructWhat it doesTouchesExample in Chowk
commandA write — mutates exactly one aggregateone aggregatePlaceOrder, MarkOrderPaid
queryA read — never mutatesreads one aggregateGetOrder, ListOrdersByCustomer
use_caseA saga — orchestrates several writes in one transactionmany aggregatesCheckout
subscriptionAn event reaction — runs when a fact is announceddispatches a commandStartFulfillmentOnOrderPlaced
policyAn authorization rule — gates who may run an operationthe callerOperatorOnly

Writes and reads are separate constructs

The first thing to notice is that a write and a read are different kinds of thing. A command changes state; a query observes it. They are never the same declaration with two modes — they are authored apart, live in commands/ and queries/, and are optimized independently. This is command–query separation made structural: the write surface and the read surface of an aggregate evolve on their own.

That is why the Chowk Order has a commands/order.vishwakarma (place, add a line, mark paid) and a separate queries/order.vishwakarma (get one, list a customer's). The same aggregate, two surfaces, two files.

One aggregate per write — and what to do when you need more

The load-bearing rule of the write side: a command mutates exactly one aggregate. PlaceOrder opens an Order; AddOrderLine creates an OrderLine; ReserveStock adjusts a StockLevel. None of them reaches across aggregate boundaries.

When a single business action genuinely must change several aggregates together — atomically, all-or-nothing — that is not a bigger command. It is a use_case: a small state machine that calls one command per step inside one transaction. Chowk's Checkout places an order and adds its first line as a single unit. The one-aggregate rule stays intact; the saga composes commands that each still obey it.

How the pieces connect

A command can announce a fact by emitting a domain event; a subscription reacts to that fact by dispatching another command — and the two sides never call each other directly. That is the event-driven spine of the whole application:

flowchart LR
  A[command<br/>PlaceOrder] -->|emits| E((event<br/>OrderPlaced))
  E -->|delivered to| S[subscription<br/>StartFulfillmentOnOrderPlaced]
  S -->|dispatches| C[command<br/>StartFulfillment]

PlaceOrder in the ordering sub-domain knows nothing about fulfillment; it just records that an order was placed. The fulfillment sub-domain listens for that fact and opens a shipment. Decoupling like this is what lets the two teams — and the two sub-domains — move independently.

The pages that follow build this surface one construct at a time: Commands and Queries for the write and read sides, Use cases and sagas for multi-aggregate transactions, Subscriptions for the event reactions, and Policies for the authorization that gates them all.