Skip to content

About

No description, website, or topics provided.

Resources

Stars

4 stars

Watchers

0 watching

Forks

Latest commit

 

History

111 Commits

Folders and files

Repository files navigation

ascetic-ddd-rust

DDD Toolkit and Seedwork for Rust — the Rust port of ascetic-ddd-python and ascetic-ddd-go.

Repository layout

A Cargo workspace with one crate per Python package:

ascetic-ddd-rust/
├── Cargo.toml            # [workspace] members = ["crates/*"], shared metadata
└── crates/
    └── saga/             # ascetic-ddd-saga  <- ascetic_ddd/saga
        ├── src/          # library modules, one per Python module
        ├── examples/     # runnable examples (cargo run --example ...)
        └── tests/        # integration tests, one per Python test module

Why one crate per package rather than one crate for everything

Python ships ascetic_ddd as a single distribution because its subpackages cost nothing until imported, and their third-party requirements are resolved lazily at runtime. Neither holds in Rust:

  • A crate is the unit of compilation and of dependency resolution. One crate for everything would force every user of saga to build (and audit, and license-check) the dependencies of kms, faker and the Postgres-backed packages. Cargo features could hide that, but they turn into a combinatorial test matrix and are easy to get wrong.
  • A crate is the unit of versioning and publication. Separate crates can reach 1.0 at their own pace; a monolith forces the least stable package to set the version of everything else.
  • A crate boundary cannot be cyclic. That is a liability in Python (where cycles between subpackages are possible and occasionally accidental) and an asset here: the dependency direction between packages is checked by the compiler, which is exactly what DDD layering wants.

The workspace keeps the ergonomics that a single distribution provides: one cargo test / cargo clippy for the whole repository, a shared Cargo.lock, and shared metadata in [workspace.package].

If a facade is wanted later — a single ascetic-ddd crate re-exporting the others behind features, so that ascetic_ddd::saga keeps working as one import — it can be added as one more member without moving any code.

Crates

Crate Python package Description
ascetic-ddd-bus trading.bus (OCaml) Scheme-dispatched message bus with in-memory and Kafka adapters
ascetic-ddd-dek seedwork/infrastructure/repository/dek_store Data-encryption keys per resource, versioned, wrapped by the tenant's KEK; the ciphers a codec seals with
ascetic-ddd-event-store seedwork/infrastructure/repository/event_store Event store on PostgreSQL: streams appended with an expected position, payloads sealed with the stream's DEK
ascetic-ddd-inbox ascetic_ddd/inbox Transactional Inbox on PostgreSQL
ascetic-ddd-kms ascetic_ddd/kms Key management for a tenant's keys: a KEK per tenant, versioned and rotated, and signatures and MACs under the tenant's keys, in PostgreSQL under a master key or in Vault Transit
ascetic-ddd-outbox ascetic_ddd/outbox Transactional Outbox on PostgreSQL
ascetic-ddd-rop trading.rop (OCaml) Railway-oriented programming: Result with accumulating, never-empty errors
ascetic-ddd-saga ascetic_ddd/saga Saga pattern (routing slip) for distributed transactions
ascetic-ddd-specification ascetic_ddd/specification, Go cmd/specgen Specification pattern: a predicate as a tree, built by hand, by typed terms, from a JSONPath template or from a Rust function; evaluated in memory or compiled to PostgreSQL
ascetic-ddd-specification-macros specification/domain/lambda_filter, Go cmd/specgen #[specification]: a predicate function and its tree from one source
ascetic-ddd-session ascetic_ddd/session Unit of Work: session scopes, savepoints, identity map, REST and composite sessions, PostgreSQL adapter (in progress)
ascetic-ddd-trace — Recorders of what the outbox and the inbox report, for trace validation against the TLA+ models

Architecture decisions

  • ADR-0001 — a session is a cloneable handle (the borrow is superseded by ADR-0004).
  • ADR-0002 — where a message is serialized and encrypted relative to the outbox.
  • ADR-0003 — the outbox and the inbox as channels of the bus, joined by bridges.
  • ADR-0004 — the scope receives its session by value, and the session promises Send.
  • ADR-0005 — a failed message holds its partition, and is parked after its attempts.
  • ADR-0006 — a message ahead of its dependency waits out of the queue, and is woken by the dependency's mark (the wait's transaction is amended by ADR-0009).
  • ADR-0007 — a dispatcher owns a slot by locking its position, and has no identity.
  • ADR-0008 — the inbox takes a slot by locking its row, and its dispatcher has no identity (the head's read is amended by ADR-0009).
  • ADR-0009 — a wait is a transaction of its own, serialized with the dependency's mark by a lock on its identity.
  • ADR-0010 — which failures stop the loops, which wait, and which park at once.
  • ADR-0011 — what a specification must do.
  • ADR-0012 — a specification is a tree over its values, read by functions.
  • ADR-0013 — a null is tested, not compared, where null is a value.
  • ADR-0014 — a name is the storage's, quoted as it is, and mapped in one place.
  • ADR-0015 — a string constant is read as the kind of the member beside it.
  • ADR-0016 — a tenant's keys are one per use: the KEK, a signing key per algorithm, a MAC key per purpose.
  • ADR-0017 — the bus's key is one per tenant on a mount of its own, derived per channel; the adapter makes no key, custody is a port beside, and a refusal is read once.
  • ADR-0018 — the intake names the row, and at an inbox that receives several channels the stage is chosen by the row; one subscriber, no filter by address, and why.
  • ADR-0019 — a signature whose version something outside the KMS already holds is made at that version and makes no key; the public keys of the versions that verify are a port of their own, derived where the adapter holds the private halves (Proposed).
  • ADR-0020 — custody of a tenant's keys is a port per kind, beside its use; under managed keys nothing that uses a key makes one; a rotation never makes the first version, and a retirement keeps the latest and never lowers the floor (Proposed).

Specifications

TLA+ models of the outbox protocol, checked with TLC, live in verify/tla: what is proved, on which instance, and what is left to tests.

Development

cargo test           # unit, integration and documentation tests
cargo clippy --all-targets
cargo fmt --all
cargo doc --open

License

MIT — see LICENSE.

About

No description, website, or topics provided.

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages