# How Sandloom works

Your product is a set of isolated modules. They reach the framework through one public SDK, and drivers connect the framework to your backends.

[Business modules](<https://sandloom.dev/docs/modules/index.md>)Your application code

**Orders**Order logic &amp; data

[**Contracts**Typed calls &amp; replies↔](<https://sandloom.dev/docs/modules/between/contracts/index.md>) [**Events**Asynchronous reactions→](<https://sandloom.dev/docs/modules/between/events/index.md>)

**Catalog**Product logic &amp; data

Standard modules, same boundariesIdentity · Tenancy · Billing

use the public SDK

[Public SDK`sandloom.sdk`](<https://sandloom.dev/docs/reference/sdk/index.md>)

**Kernel**Provided by Sandloom

TransactionsAccess checksOutbox &amp; inboxLocal / RPC routing

Each application or service runs its own kernel.

resolve infrastructure capabilities

[Drivers](<https://sandloom.dev/docs/infrastructure/index.md>)Selected by configuration

Each driver connects the runtime to a concrete backend.

Database · sqlalchemy**PostgreSQL**

Cache**Redis**

Events · streams**Redis Streams**

Files · local**Disk / volume**

Tasks · celery**Celery**

Email**SMTP**

One modular application. The same module code in a monolith or separate services.

## Design principles

- **A module stands alone.** It imports only the public SDK and generated contracts, never another module. You or a coding agent can change one by reading it and the contracts it calls.
- **The framework owns what is easy to get wrong.** It commits the transaction, authenticates each route unless the route declares `public(reason=...)`, and delivers events through an outbox. Weakening a default takes a visible line of code. Which resources a user may touch is yours to declare with [`require`](<https://sandloom.dev/docs/modules/http/authorization/index.md>).
- **Configuration decides where things run.** It selects which process loads which module and which backend each capability uses. Moving a running module's existing data to its own database is a separate, [explicit step](<https://sandloom.dev/docs/operate/split-into-services/#existing-data>).
- **A machine checks the rules.** `sandloom check` and the startup checks enforce the boundaries and name the fix: `sandloom explain <code>`.

## Modules and drivers

A [module](<https://sandloom.dev/docs/modules/index.md>) is what your product does; a [driver](<https://sandloom.dev/docs/infrastructure/index.md>) is what it runs on. Modules call the framework's capabilities through the SDK and never name a driver. `SANDLOOM_MODULES` selects which modules a process loads, and `INFRA` selects which driver backs each capability. Modules talk to each other through events, contracts, projections and sagas, never an import ([Choose how modules talk](<https://sandloom.dev/docs/modules/between/index.md>)).

## What do I write?

| I want to | I write |
| --- | --- |
| add a product feature | a [module](<https://sandloom.dev/docs/modules/create/index.md>) |
| react to something another module did | an [event consumer](<https://sandloom.dev/docs/modules/between/add-consumer/index.md>) |
| ask another module for data now | a call to its [contract](<https://sandloom.dev/docs/modules/between/add-contract/index.md>) |
| run a process across modules, with undo | a [saga](<https://sandloom.dev/docs/modules/between/add-saga/index.md>) |
| show a list that spans modules | a [projection](<https://sandloom.dev/docs/modules/between/add-projection/index.md>) |
| change how identity, tenancy or billing behave | a [customization](<https://sandloom.dev/docs/modules/batteries/customize/index.md>) |
| send notifications through a new service | a [driver](<https://sandloom.dev/docs/infrastructure/write-a-driver/index.md>) |
| run a module as its own service | [configuration](<https://sandloom.dev/docs/operate/split-into-services/index.md>), no module code |
