# Decent Billing ERP - Architecture

## 1. Goals

- Predictable behaviour across every module, from POS to GST returns.
- A clean boundary between framework, application, and business logic.
- A test surface that lets us verify business rules without booting HTTP.
- A code layout where new modules can be added by copy-paste of a directory,
  never by editing shared switch-statements or god-classes.

## 2. Layers

```
Presentation      HTTP controllers, Blade views, JSON API responses,
                  console commands, Livewire (in later sprints).
    |
Application       Use-cases, orchestration, request DTOs, policies at
                  method boundaries.
    |
Domain            Entities, value objects, invariants, domain events,
                  domain exceptions. No framework imports allowed here.
    |
Infrastructure    Eloquent models, repositories, external gateways
                  (GST, SMS, WhatsApp, payments), file storage, mail.
```

In S0.1 only the outermost frame exists: the Presentation layer (controllers,
layouts, views), the shared Infrastructure spine (config, logging, exceptions,
security headers), and the Domain "commons" (base exception types).

From Sprint 1 onwards every business module (Products, Parties, Invoices, ...)
introduces its own `Domain/<Module>` and `Application/<Module>` folders. The
Presentation layer only depends on the Application layer.

## 3. Module template

Each future module follows this layout:

```
app/Domain/<Module>/
  Entities/
  ValueObjects/
  Events/
  Exceptions/
app/Application/<Module>/
  UseCases/
  Requests/       # DTOs, not FormRequests
  Policies/
app/Infrastructure/<Module>/
  Eloquent/
  Repositories/
  Gateways/
app/Http/Controllers/<Module>/
resources/views/<module>/
routes/modules/<module>.php
tests/Unit/<Module>/
tests/Feature/<Module>/
```

Cross-module calls happen only through the Application layer of the other
module, never by reaching into its Domain or Infrastructure. When two modules
need to talk asynchronously, they do so through domain events.

## 4. Configuration and settings

`config/decent.php` is the single source of static product configuration.
`App\Support\SystemConfig` is the read-through service every layer uses. In
S0.2 we will layer a `system_settings` DB-backed override on top of the same
service without touching call sites.

## 5. Logging and observability

- Every request receives a correlation id (from `X-Request-Id` or a fresh
  UUID). It is bound into the container and emitted on every log line by
  `CorrelationIdProcessor`.
- `RedactSensitiveProcessor` scrubs a fixed set of sensitive keys (passwords,
  tokens, GSTIN, PAN, Aadhaar, bank accounts, IFSC, OTPs, card data) from log
  context before serialisation.
- Logs are JSON-formatted so they can be shipped to any structured backend
  (Loki, ELK, CloudWatch, Datadog) unchanged.

## 6. Error handling

All exceptions flow through `App\Exceptions\Renderer\ExceptionRenderer`:

- API / JSON clients receive a stable envelope: `{ errors: [{ code, message,
  trace_id, context }] }`.
- Web clients receive a themed error page under `resources/views/errors/*`.
- Domain exceptions carry `errorCode`, `httpStatus`, and structured `context`,
  so the same error surfaces identically over JSON and HTML.
- Unexpected 5xx responses never leak internals in production (only when
  `APP_DEBUG=true`).

## 7. Security

- `ApplySecurityHeaders` writes CSP (nonce-based with `strict-dynamic`),
  X-Content-Type-Options, X-Frame-Options, Referrer-Policy,
  Permissions-Policy, COOP, CORP, X-Permitted-Cross-Domain-Policies, and HSTS
  (when `DECENT_FORCE_HTTPS=true`).
- CSRF is enforced by Laravel's default web middleware for state-changing
  POST/PUT/PATCH/DELETE routes.
- Sessions run over HTTPS with `secure` and `same-site=lax` cookies in
  production.

See `SECURITY.md` for the full posture.

## 8. Test strategy

- **Unit tests** exercise pure classes with no framework dependency
  (`Money\IndianDigitGrouping`, `Logging\RedactSensitiveProcessor`, domain
  exceptions, helpers).
- **Feature tests** boot the full HTTP kernel against SQLite in-memory and
  cover the request lifecycle end-to-end (routes, middleware, controllers,
  requests, views).
- Every domain module added in later sprints must ship with both suites and
  keep line coverage of `app/Domain/*` at 100%.

## 9. Non-goals for S0.1

- No authentication (S0.2).
- No RBAC (S0.2).
- No tenant scoping (S0.3).
- No installer UI (S0.5).
- No business modules (Sprint 1+).
