# Asase — Architecture

> Food and agriculture operations intelligence domain for Ghana's food value
> chain. Named after Asase Yaa, the Akan earth goddess of fertility and
> sustenance.

---

Asase is the platform layer for Ghana's entire agricultural value chain. It
exists because agriculture is uniquely complex in Ghana: the country operates
with two distinct rainfall regimes (bimodal in the south, unimodal in the
north), local measurement units (olonka, maxi-bag, mini-bag) that differ from
international standards, seven active regulatory bodies with overlapping
jurisdiction, and a post-harvest spoilage rate of 20–30% on some perishable
crops. A generic agriculture framework cannot encode these realities — Asase is
built Ghana-first from the ground up.

The domain exposes its capabilities to five consumer applications: an API
gateway, a management dashboard, a field mobile application, a digital
marketplace, and a processing plant interface. Other Oshun domains reach into
Asase through typed connector classes in `@asase/connectors`, rather than by
importing internal libraries directly.

---

## 1. Domain Summary

Asase is an implemented domain with 15 libraries under `libs/asase/` and 5
application packages under `apps/asase/`. It provides the domain layer for
Ghana's food and agriculture value chain — covering 19 business units from farm
production through processing, cold chain, logistics, quality assurance, export,
agricultural inputs, retail/QSR, financial modelling, and market operations.

The domain is designed around the specific reality of Ghana's agricultural
sector: dual rainy seasons in the south, a single short season in the north, 16
administrative regions with distinct agro-ecological zones, Ghana-specific units
of measure (olonka, maxi-bag, mini-bag), COCOBOD cocoa grading standards, MoFA
farm registration, FDA licensing requirements, and GHS as the primary currency.

---

## 2. Current Structure

The workspace is organised as a dependency tree with `@asase/core` at the root.
Specialised libraries build on top of `@asase/core` for their domain-specific
logic, while `@asase/infrastructure` bridges Asase to shared Oshun platform
services (cache, event bus, logging, metrics).

```
libs/asase/
├── core/                   # @asase/core — foundation domain logic
│   └── src/
│       ├── index.ts             # Public API barrel
│       ├── types.ts             # Branded IDs, enums, value objects, entity types
│       ├── constants.ts         # Crop varieties, breeds, regulatory bodies, markets
│       ├── business-units.ts    # Per-unit config interfaces and type guards
│       ├── agro-ecology.ts      # Zone classification and regional profiles
│       ├── measurement.ts       # Unit types, measurement algebra, conversions
│       ├── seasonality.ts       # Crop seasonality profiles and window resolution
│       ├── stakeholders.ts      # Stakeholder types and compliance checks
│       ├── domain-entities.ts   # Crop catalogs, facilities, products, price trends
│       ├── domain-services.ts   # UnitConversion, SeasonalCalendar, Regulatory, Geo, Audit
│       ├── unit-conversion-data.ts  # Shared conversion-factor maps
│       ├── db-schema.ts         # Drizzle ORM tables (36 tables, pgvector support)
│       ├── validation.ts        # Domain-specific validation functions
│       ├── utils.ts             # Agricultural calculation utilities
│       ├── geo-utils.ts         # Haversine distance, DMS, Ghana bounds
│       └── errors.ts            # Domain error hierarchy
├── crops/                  # @asase/crops — registry, variety, pest/disease, field ops, irrigation, soil, yield
├── livestock/              # @asase/livestock — livestock, poultry, hatchery, disease, aquaculture
├── infrastructure/         # @asase/infrastructure — cache, storage, events, metrics, geospatial
├── processing/             # @asase/processing — plant digital twin, scheduling, SPC, OEE
├── cold-chain/             # @asase/cold-chain — temperature, warehouse, GCX receipt, route
├── supply-chain/           # @asase/supply-chain — procurement, distribution, price, demand
├── quality/                # @asase/quality — HACCP, LIMS, recall, certification, calibration
├── export/                 # @asase/export — trade docs, AfCFTA, customs, licensing
├── inputs/                 # @asase/inputs — fertilizer, seed, agrochemical, credit
├── market-intel/           # @asase/market-intel — price intelligence, AfCFTA, policy
├── retail/                 # @asase/retail — QSR, franchise, catering, delivery
├── financials/             # @asase/financials — economics, projection, capex, impact
├── connectors/             # @asase/connectors — cross-domain bridges
├── sota/                   # @asase/sota — satellite, drone, CV, blockchain, ML/RL
├── migrations/             # @asase/migrations — Drizzle migration runner and seeds
├── README.md               # Domain overview and business unit listing
└── tsconfig.json           # Base TypeScript config
```

Applications under `apps/asase/` provide five implemented surfaces: `api`,
`dashboard`, `field`, `marketplace`, and `processing`.

---

## 3. Module Architecture

`@asase/core` remains the foundation library, and the domain has expanded into
purpose-built packages for crops, livestock, infrastructure, processing, cold
chain, supply chain, quality, export, inputs, market intelligence, retail,
financials, cross-domain connectors, and SOTA technology, plus the
`@asase/migrations` schema package and five application packages.

### Module Dependency Graph (within `@asase/core`)

The files inside `@asase/core` are deliberately layered so that lower-level
modules (types, errors, raw data) carry no imports from higher-level ones. This
means any file in the graph can be imported in isolation without pulling in the
full domain. The graph below shows which modules each file depends on.

```
db-schema.ts
  └── (standalone Drizzle table definitions)

errors.ts
  └── (standalone error hierarchy)

geo-utils.ts
  └── (standalone geographic utilities)

unit-conversion-data.ts
  └── (raw conversion factor maps)

types.ts
  └── (core enums, branded types, value objects)

constants.ts
  └── types.ts

agro-ecology.ts
  └── types.ts

measurement.ts
  └── unit-conversion-data.ts

seasonality.ts
  └── (standalone crop profiles)

stakeholders.ts
  └── types.ts

domain-entities.ts
  └── types.ts, constants.ts

validation.ts
  └── constants.ts

utils.ts
  └── constants.ts, types.ts, agro-ecology.ts

domain-services.ts
  └── types.ts, measurement.ts, agro-ecology.ts,
      seasonality.ts, constants.ts, geo-utils.ts
```

---

## 4. Design Principles

### Ghana-First Domain Model

The type system is calibrated to Ghana's reality from the ground up rather than
being a generic agriculture model with Ghana options bolted on. This means
enums, validation patterns, and constants reflect what Ghanaian farmers,
regulators, and traders actually encounter:

- `Region` enum covers exactly Ghana's 16 administrative regions
- `Season` enum uses the names Ghanaian farmers use (`major_rainy`,
  `minor_rainy`, `harmattan`) not generic seasons
- `MassUnit` includes `maxi_bag_100kg` and `mini_bag_50kg`; `VolumeUnit` adds
  the traditional `olonka`, `bowl`, `american_tin_small`, and
  `american_tin_large` measures alongside international units
- Phone number validation handles MTN, Vodafone, and AirtelTigo Ghana number
  ranges
- Farm registration format follows the `GH-<region>-<district>-<serial>`
  pattern, validated against the two-letter `REGION_CODES`

### Branded IDs and Structured Value Objects

Every entity identifier uses a branded string type — `Brand<string, '<Name>'>` —
to prevent ID confusion at compile time. Passing a `PlotId` where a `FarmId` is
expected becomes a type error, catching a whole class of bugs before runtime.
Each branded type comes with a `create<Name>Id` constructor. Value objects that
carry domain semantics are structured interfaces rather than branded numbers,
ensuring the units and context travel with the data:

```typescript
type FarmId = Brand<string, 'FarmId'>; // build via createFarmId()

interface Temperature {
  celsius: number;
} // temperatureFromCelsius()
interface SoilPH {
  value: number;
  classification: SoilPHClassification;
} // createSoilPH()
interface CurrencyAmount {
  amount: number;
  currency: CurrencyCode;
}
interface PlotArea {
  value: number;
  unit: AreaUnit;
}
interface YieldPerHectare {
  kgPerHectare: number;
}
```

The `measurement.ts` module additionally provides phantom-typed numeric
measurements (`Measurement<U, D>`) for compile-time unit safety in mass, volume,
area, temperature, humidity, and currency calculations.

### Discriminated Union for Business Units

Asase covers 19 distinct business units — from a bakery to a cold-chain operator
to a poultry farm — each with different operational parameters and regulatory
obligations. Rather than storing configuration in a generic map or using runtime
duck-typing, the 19 business unit configurations use a discriminated union type.
This provides exhaustiveness checking in switch statements and precise access to
unit-specific fields without runtime casting:

```typescript
type BusinessUnitConfig =
  | BakeryConfig // { type: 'bakery'; ovenCount: number; ... }
  | CafeConfig // { type: 'cafe'; seatingCapacity: number; ... }
  | PlantFarmsConfig // { type: 'plant_farms'; totalAreaHectares: number; primaryCrops; ... }
  | PoultryConfig; // { type: 'poultry'; broilerBirdsPerCycle: number; layingHens: number; ... }
// ... all 19

// Type-narrowing via type predicates
function isBakeryConfig(config: BusinessUnitConfig): config is BakeryConfig {
  return config.type === 'bakery';
}
```

### Measurement Algebra

Agricultural calculations are especially prone to unit confusion: confusing
kg/ha with t/ha can turn a profitable crop projection into a loss. The
`measurement.ts` module implements a phantom-typed measurement algebra over the
`Measurement<U, D>` brand. Conversion functions take the source value plus
explicit `from`/`to` units and route through a canonical SI base, so there is
one correct conversion path and no ambiguity:

```typescript
// Construct measurements
const m: Mass = mass(50, MassUnit.KILOGRAMS);
const a: Area = area(2.5, AreaUnit.HECTARES);

// Convert via canonical base unit
const tonnes = convertMass(m, MassUnit.KILOGRAMS, MassUnit.TONNES);
const acres = convertArea(a, AreaUnit.HECTARES, AreaUnit.ACRES);

// Format a numeric value + unit for display
formatQuantity(50, MassUnit.KILOGRAMS); // → "50.00 kg"
```

### Service Class Architecture

Domain services are implemented as classes rather than functions to support
dependency injection and future testability. All services can be instantiated
with different data sources:

```typescript
// All services can be instantiated with different data sources
const regulatory = new GhanaRegulatoryService(); // uses built-in constants
const geo = new GeolocationService(); // uses built-in region data
const audit = new AuditTrailService(customAdapter); // injectable storage
```

---

## 5. Database Schema Design

The Drizzle ORM schema in `db-schema.ts` uses the `asase_` prefix throughout —
27 `pgEnum` types and 36 tables. Schema migrations and seed data live in the
separate `@asase/migrations` package (11 migration files, 8 seed scripts, RLS
policy migration, connection-pool presets).

The schema makes three notable design choices that affect how the data layer
behaves at runtime:

- **pgvector integration**: Crop similarity embeddings stored as 1536-dimension
  vectors for nearest-neighbour variety recommendations. The `vector` custom
  type bridges Drizzle's type system with the `pgvector` PostgreSQL extension.
  This enables semantic crop searches (e.g., "find varieties similar to Obatanpa
  that suit Upper East Region") without full-table scans.

- **Enum types in PostgreSQL**: Strongly typed fields use `pgEnum` for status
  columns (`asase_business_unit`, `asase_growth_stage`, `asase_quality_grade`,
  `asase_shipment_status`) rather than plain strings. This moves enum
  enforcement into the database layer, preventing invalid status values from
  being stored even if application-level validation is bypassed.

- **JSONB for flexible metadata**: Business unit configuration and product
  attributes stored as JSONB to accommodate per-unit schema variation without
  requiring separate tables for each of the 19 configuration types.

---

## 6. Technology Stack

The table below shows the technology choice for each major concern. Drizzle ORM
was chosen over Prisma primarily for its explicit, code-first schema definition
(no separate schema file) and its first-class support for raw SQL when needed.

| Component  | Technology                                 |
| ---------- | ------------------------------------------ |
| Language   | TypeScript (ESM)                           |
| ORM        | Drizzle ORM                                |
| Database   | PostgreSQL (with pgvector extension)       |
| Validation | Zod (consumed internally in validation.ts) |
| Build      | `@nx/js:tsc`                               |
| Testing    | Vitest                                     |

---

## 7. Project Configuration

- **Project tags**: `["scope:asase", "layer:domain", "type:lib"]`
- **Module format**: ESM (`"type": "module"`)
- **Build executor**: `@nx/js:tsc`

### Build Commands

If the Nx orchestrator has worktree conflicts, bypass it and run the tools
directly from the library directory.

```bash
# Test
pnpm nx test @asase/core

# Build
pnpm nx build @asase/core

# Lint
pnpm nx lint @asase/core

# Type check (if Nx has worktree conflicts)
cd libs/asase/core && npx tsc --noEmit
```

---

## 8. Library Footprint

The domain has expanded from its initial `@asase/core` foundation to 15
purpose-built libraries plus a migration package, and 5 application packages.

The dependency structure is intentionally flat to keep build times short and
avoid circular dependencies. `@asase/core` is the dependency root: most
libraries declare `@asase/core` as a `workspace:*` dependency. Three libraries
are standalone with no `@asase/core` dependency — `@asase/financials`,
`@asase/connectors`, and `@asase/sota` — because their concerns are sufficiently
distinct that they benefit from clean isolation. `@asase/infrastructure`
additionally depends on the shared `@oshun/cache`, `@oshun/event-bus`,
`@oshun/logging`, and `@oshun/metrics` packages. `@asase/migrations` depends on
`@asase/core`, `@oshun/database`, and `@oshun/logging`.

ML and remote-sensing capabilities live in `@asase/sota` (satellite, drone,
computer-vision grading, blockchain traceability, ML yield prediction, RL crop
planning). The capabilities once sketched as a "community" library (farmer
profiles, cooperatives, extension) are covered by stakeholder types in
`@asase/core`, outgrower/contract-farming in `@asase/supply-chain`, and farmer
registration / advisory in the `@asase/field` application.

---

## 9. Related Domains

Cross-domain integration is implemented in `@asase/connectors`, which provides
classes and builder functions bridging Asase to six Oshun domains. The connector
boundary exists so that Asase does not directly import other domain libraries
(which would create a circular dependency or force Asase to depend on domain
logic it does not own). Instead, each connector owns a typed payload interface
that translates Asase data structures into the vocabulary the receiving domain
expects. The six boundaries and the data that crosses each are:

| Domain    | Why the boundary exists and what crosses it                                                                                                                                                                                                         |
| --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Brigid    | Brigid owns engineering and physical infrastructure design. Asase sends requirements (irrigation system specs, processing-facility layout constraints, cold-chain siting needs, rural road load ratings); Brigid returns engineering proposals.     |
| Cybele    | Cybele owns earth and environmental intelligence. Asase sends crop location and land-use data; Cybele returns climate-adaptation recommendations, soil health assessments, water-resource analysis, and biodiversity impact scores.                 |
| Freya     | Freya owns commerce and payment flows. Asase sends marketplace product listings, payment reconciliation requests, and supply-chain financing needs (warehouse receipts, purchase orders); Freya returns transaction results and settlement records. |
| Saraswati | Saraswati owns knowledge and education content. Asase sends structured crop and farming practice data; Saraswati returns farmer training materials and agricultural research outputs formatted for advisory delivery.                               |
| Maat      | Maat owns governance and compliance. Asase sends food-safety compliance reports and land-tenure documentation packages; Maat manages the regulatory lifecycle, audit responses, and dispute records.                                                |
| Aje       | Aje owns financial intelligence and credit. Asase sends farmer profiles and commodity data; Aje returns agricultural lending scorecards and commodity hedging structures appropriate to Ghana's forward markets.                                    |
