Rust infrastructure toolkit extracted from the Phenotype ecosystem. The workspace now provides shared domain, application, port, and infrastructure crates that support hexagonal and clean architecture across the broader polyrepo.
| Crate | Description |
|---|---|
phenotype-domain |
DDD value objects, entities, aggregates, events, and domain errors |
phenotype-application |
CQRS commands, queries, DTOs, and application handlers |
phenotype-port-interfaces |
Hexagonal inbound/outbound port traits and shared contracts |
phenotype-event-sourcing |
Append-only event store with hash-chain verification and snapshot management |
phenotype-cache-adapter |
Two-tier cache with TTL expiration and observability hooks |
phenotype-policy-engine |
Rule-based policy evaluation engine with TOML config loading |
phenotype-state-machine |
Generic finite state machine with transition guards and history tracking |
phenotype-postgres-adapter |
PostgreSQL persistence adapter |
phenotype-redis-adapter |
Redis persistence / cache adapter |
phenotype-http-adapter |
HTTP adapter and transport utilities |
Add any crate as a git dependency:
[dependencies]
phenotype-domain = { git = "https://github.com/KooshaPari/phenotype-shared" }
phenotype-application = { git = "https://github.com/KooshaPari/phenotype-shared" }
phenotype-port-interfaces = { git = "https://github.com/KooshaPari/phenotype-shared" }
phenotype-event-sourcing = { git = "https://github.com/KooshaPari/phenotype-shared" }
phenotype-cache-adapter = { git = "https://github.com/KooshaPari/phenotype-shared" }
phenotype-policy-engine = { git = "https://github.com/KooshaPari/phenotype-shared" }
phenotype-state-machine = { git = "https://github.com/KooshaPari/phenotype-shared" }
phenotype-postgres-adapter = { git = "https://github.com/KooshaPari/phenotype-shared" }
phenotype-redis-adapter = { git = "https://github.com/KooshaPari/phenotype-shared" }
phenotype-http-adapter = { git = "https://github.com/KooshaPari/phenotype-shared" }use phenotype_event_sourcing::{EventEnvelope, EventStore};
use phenotype_event_sourcing::memory::InMemoryEventStore;
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
struct UserCreated { user_id: String, email: String }
let store = InMemoryEventStore::new();
let event = EventEnvelope::new(
UserCreated { user_id: "u-1".into(), email: "a@b.com".into() },
"system",
);
let seq = store.append(&event, "UserCreated", "u-1").unwrap();
assert_eq!(seq, 1);
// Retrieve events
let events = store.get_events::<UserCreated>("UserCreated", "u-1").unwrap();
assert_eq!(events.len(), 1);use phenotype_cache_adapter::TieredCache;
use std::time::Duration;
let cache = TieredCache::new(100, Duration::from_secs(300), None);
cache.insert("key".to_string(), "value".to_string());
assert_eq!(cache.get(&"key".to_string()), Some("value".to_string()));use phenotype_policy_engine::{PolicyEngine, PolicyContext};
let engine = PolicyEngine::new();
engine.load_toml_str(r#"
[[rules]]
name = "require-auth"
action = "require"
field = "auth_token"
"#).unwrap();
let mut ctx = PolicyContext::new();
ctx.set("auth_token", "abc123");
let result = engine.evaluate(&ctx).unwrap();
assert!(result.passed());use phenotype_state_machine::{State, StateMachine};
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
enum Status { Draft, Review, Approved, Done }
impl State for Status {
fn ordinal(&self) -> u32 {
match self { Self::Draft => 0, Self::Review => 1, Self::Approved => 2, Self::Done => 3 }
}
}
let mut sm = StateMachine::new(Status::Draft);
sm.transition(Status::Review).unwrap();
assert_eq!(sm.current(), &Status::Review);cargo fmt --check
cargo clippy --workspace -- -D warnings
cargo test --workspaceThe workspace is organized as a layered hexagonal stack:
- Domain:
phenotype-domainowns value objects, entities, aggregates, events, and domain errors. - Application:
phenotype-applicationcoordinates commands, queries, DTOs, and handlers. - Ports:
phenotype-port-interfacesholds technology-agnostic inbound and outbound contracts. - Infrastructure: event sourcing, cache, policy, state machine, database, Redis, and HTTP adapters live below the core layers.
phenotype-shared/
Cargo.toml
crates/
phenotype-domain/
phenotype-application/
phenotype-port-interfaces/
phenotype-event-sourcing/
phenotype-cache-adapter/
phenotype-policy-engine/
phenotype-state-machine/
phenotype-postgres-adapter/
phenotype-redis-adapter/
phenotype-http-adapter/
The initial governance PR for phenotype-shared should stay intentionally small and canonical:
- add a repo-level quality gate workflow that runs format, lint, build, and tests
- add an ADR stub for governance, branch protection, and canonical repo policy
- add lightweight policy-gate checks for top-level repo layout and required governance files
- add architecture lint placeholders for boundary and dependency drift
- keep repo-specific behavior local; only extract cross-cutting rules into shared code
phenotype-shared/crates/phenotype-port-interfacesis the canonical home for domain-agnostic traits and value objects.- Repo-local port layers such as
phench/src/phench/application/ports.pyandworktree-manager/src/worktree_manager/ports/mod.rsshould keep their names and responsibilities aligned with the shared port vocabulary. - Prefer extracting only truly generic contracts into the shared ports crate; keep workflow- or repo-specific ports local.
- First: pagination primitives and response wrappers shared across application/query boundaries.
- Next: error mapping helpers, keeping domain-specific errors local.
- Then: reusable infrastructure primitives that already repeat across repos.
- Keep canonical top-level buckets explicit:
apps/,libs/,infrastructure/,governance/,tooling/,templates/. - Keep active worktrees under
repos/worktrees/<project>/<category>/<wtree>so canonical repo roots stay clean. - Prefer shallow, discoverable top-level directories over deeply nested ad hoc layouts.
- Keep domain-specific exceptions local to each crate or package, such as
phench/src/phench/domain/exceptions.pyandworktree-manager/src/worktree_manager/domain/errors.rs. - Extract only cross-cutting, technology-neutral error categories into shared layers, such as validation/storage/not-found/permission state errors.
- Avoid creating parallel generic error enums in multiple repositories unless they serve different bounded contexts.
MIT