OpenFief Give your AI agents a fief, not the kingdom.

Code organization

Organized by domain, following DDD (domain-driven design). Names come from the glossary. Rules without a marker are the author’s; (P) marks a Claude Code proposal awaiting the author’s approval.

Bounded contexts#

ContextOwnsMilestone
ledgerAccounts, ledger accounts, transactions, postings, instruments, holdings, lots, balance assertionsM0
valuationPrices, index levels, FX rates, bond series and statutory valuation, valuationsM0
ingestInstitutions, connectors’ ingest paths, import batches, mapping broker data to the ledgerM0
viewsThe read API and view plugins (built-in UI, Ghostfolio)M0
pluginsThe operator: Plugin, Grant, manifests, egress allow-lists, image verificationM0
identityOwner, passkeys, script tokens, plugin identityM0
portfolioSleeves, the model portfolio and its targets, backtest and max drawdownM0 (model portfolio optional)
jurisdiction/plPolish rules: bond terms, tax wrappers; later PIT-38M0 (bonds)
fiefs, execution, orchestration, models, auditLeases, intents/orders/policy, agents and wakes, model routing, audit logM1+

Context map (P). ingest writes to ledger and valuation through their app APIs. valuation reads ledger. portfolio reads valuation for prices and FX rates. views reads ledger, valuation and portfolio. jurisdiction/pl is pure rules used by valuation. plugins and identity are independent. Dependencies never point the other way. A tiny shared kernel internal/kernel holds money, currency, quantity and date types.

Layout (P)#

cmd/<binary>/            wiring only: config, adapters, start
internal/<context>/      domain: types and pure functions (package <context>)
internal/<context>/app/  use cases; ports (interfaces) declared here, by the consumer
internal/<context>/<adapter>/   postgres, connectapi, nbp, ibkrflex, ...
internal/kernel/         shared kernel: money, currency, quantity, date. Pure.
proto/openfief/<context>/v1/    contracts (Apache-2.0, own LICENSE file, D-5)
sdk/go/, sdk/ts/         plugin SDKs (Apache-2.0, own LICENSE file, separate modules)
plugins/<name>/          first-party plugins, built on the SDK
deploy/                  CRDs, bootstrap chart
test/e2e/                end-to-end suites (local k3s)
tools/<name>/            development tools, never shipped (docs generator)
site/                    the docs site: Hugo config, theme, site-only pages (docs/dev/docs.md)

Rules#

Domain packages (internal/<context>):

  • Pure functions over values. No I/O, network, database, logging, globals, goroutines or context.Context.
  • No time.Now() or randomness: take the date or seed as a parameter.
  • Business failures are returned errors with typed values, never panics.

Money (P). Never float64 for amounts, prices, rates or quantities. Use the kernel’s decimal-backed types, and every amount carries its currency.

Layering:

  • app imports domain packages.
  • Adapters import app and domain packages.
  • Only cmd imports adapters.
  • A context never imports another context’s adapters or reads its tables: each context owns its schema (P).
  • Enforced by lint (.golangci.yml).

Interfaces. Declare an interface where it’s consumed (in app), keep it small, and return concrete types.

Anti-corruption. Broker and vendor formats are parsed and translated in their adapter or plugin. Domain code never sees an IBKR, XTB or Ghostfolio shape.

Banned packages. No util, common, helpers, models or types. Find the context the code belongs to.

Generated code lives in gen/ directories and is never edited by hand.

Secrets and data.

  • Never log amounts, holdings, account numbers or tokens. Log internal IDs only (GR-12, UC-0).
  • Fixtures use synthetic data only.

Requirements. Every behavioral function is owned by a kibi requirement (kibi.md).

New context. Add it to the table above and its terms to the glossary, and get the author’s OK. Contexts are design decisions, not refactors.

Source: docs/dev/code-organization.md · last changed 2026-10-28 in 15fc52f