Party pattern: Person, Organization, Group with relationships and contact info
| Tier | identity |
| Role | unclassified (baselined) |
| Path | crates/identity/parties |
| Edition | 2021 |
| Targets | party-cli, seed_demo_crm, identity_parties, integration_tests |
| Public items | 66 across 4 modules |
| Tests | 129 |
What it is for
# identity-parties
Party pattern implementation: Person, Organization, Group with relationships.
The Party Pattern separates real-world identity from system accounts:
Person= Real-world human (name, DOB, contact info)Organization= Formal business entityGroup= Informal grouping (household, team)
Key Design Principle
Person ≠ User
This separation enables:
- One person having multiple user accounts
- User accounts without persons (service accounts)
- Persons without accounts (contacts, leads)
Example
use identity_parties::{
Person, Organization, Group, PartyRelationship, PartyId,
OrganizationType, GroupType, RelationshipType,
};
// Create a person
let person = Person::new("John")
.with_last_name("Doe")
.with_email("john@example.com");
// Create an organization
let org = Organization::new("Acme Corp")
.with_type(OrganizationType::Company);
// Create a relationship (using PartyId for type-safe party references)
let employment = PartyRelationship::employee(
PartyId::new(person.id),
PartyId::new(org.id),
).with_title("Software Engineer");
Capabilities
EffectiveAccess
Party-based authorization service
| Item |
|---|
pub struct EffectiveAccess |
PartyAuthService
Party-based authorization service
| Item |
|---|
pub struct PartyAuthService |
PartyAuthService :: fn new(pool : PgPool) -> Self |
PartyAuthService :: async fn get_party_id_for_user(& self, user_id : Uuid) -> Result <Option <Uuid>, PartyError> |
PartyAuthService :: async fn user_has_business_role(& self, user_id : Uuid, role_code : & str,) -> Result <bool, PartyError> |
PartyAuthService :: async fn get_user_business_roles(& self, user_id : Uuid) -> Result <Vec <String>, PartyError> |
PartyAuthService :: async fn party_has_role(& self, party_id : Uuid, role_code : & str,) -> Result <bool, PartyError> |
PartyAuthService :: async fn get_effective_access(& self, user_id : Uuid,) -> Result <Option <EffectiveAccess>, PartyError> |
PartyAuthService :: async fn assign_party_role(& self, party_id : Uuid, role_code : & str, notes : Option <& str>,) -> Result <Uuid, PartyError> |
PartyAuthService :: async fn remove_party_role(& self, party_id : Uuid, role_code : & str,) -> Result <bool, PartyError> |
PartyAuthService :: async fn create_user_for_party(& self, party_id : Uuid, email : & str, password_hash : & str, permission_tier : & str,) -> Result <Uuid, PartyError> |
PartyAuthService :: async fn require_business_role(& self, user_id : Uuid, role_code : & str,) -> Result <Uuid, PartyError> |
auth::roles (other)
_No module-level documentation is present in the source._
| Item |
|---|
pub const CUSTOMER: & str |
pub const VENDOR: & str |
pub const EMPLOYEE: & str |
pub const INSPECTOR: & str |
pub const MANUFACTURER: & str |
auth::tiers (other)
_No module-level documentation is present in the source._
| Item |
|---|
pub const ADMIN: & str |
pub const OPERATOR: & str |
pub const VIEWER: & str |
verb:constants
Repository functions for party models.
| Item |
|---|
pub const PERSON_SEARCH_COLUMNS: & & str |
pub const ORGANIZATION_SEARCH_COLUMNS: & & str |
verb:create
Repository functions for party models.
| Item |
|---|
async fn create_party(pool : & PgPool, party : & Party) -> Result <Party, PartyError> |
async fn create_person_for_tenant(pool : & PgPool, tenant_id : Uuid, person : & Person,) -> Result <Person, PartyError> |
async fn create_organization_for_tenant(pool : & PgPool, tenant_id : Uuid, org : & Organization,) -> Result <Organization, PartyError> |
async fn create_group(pool : & PgPool, group : & Group) -> Result <Group, PartyError> |
async fn create_relationship_for_tenant(pool : & PgPool, tenant_id : Uuid, rel : & PartyRelationship,) -> Result <PartyRelationship, PartyError> |
async fn create_animal(pool : & PgPool, animal : & Animal) -> Result <Animal, PartyError> |
verb:find
Repository functions for party models.
| Item |
|---|
async fn find_party_by_id_for_tenant(pool : & PgPool, tenant_id : Uuid, id : PartyId,) -> Result <Option <Party>, PartyError> |
async fn find_person_by_id_for_tenant(pool : & PgPool, tenant_id : Uuid, id : Uuid,) -> Result <Option <Person>, PartyError> |
async fn find_person_by_email_for_tenant(pool : & PgPool, tenant_id : Uuid, email : & str,) -> Result <Option <Person>, PartyError> |
async fn find_organization_by_id_for_tenant(pool : & PgPool, tenant_id : Uuid, id : Uuid,) -> Result <Option <Organization>, PartyError> |
async fn find_group_by_id(pool : & PgPool, id : Uuid) -> Result <Option <Group>, PartyError> |
async fn find_relationship_by_id_for_tenant(pool : & PgPool, tenant_id : Uuid, id : Uuid,) -> Result <Option <PartyRelationship>, PartyError> |
async fn find_relationships_for_party(pool : & PgPool, party_id : PartyId,) -> Result <Vec <PartyRelationship>, PartyError> |
async fn find_relationships_for_person(pool : & PgPool, person_id : Uuid,) -> Result <Vec <PartyRelationship>, PartyError> |
async fn find_relationships_involving_party(pool : & PgPool, party_id : PartyId,) -> Result <Vec <PartyRelationship>, PartyError> |
async fn find_relationships_for_animal(pool : & PgPool, animal_id : Uuid,) -> Result <Vec <PartyRelationship>, PartyError> |
async fn find_relationship(pool : & PgPool, from_party_id : PartyId, to_party_id : PartyId, relationship_type : RelationshipType,) -> Result <Option <PartyRelationship>, PartyError> |
async fn find_animal_by_id(pool : & PgPool, id : Uuid) -> Result <Option <Animal>, PartyError> |
verb:list
Repository functions for party models.
| Item |
|---|
async fn list_parties_for_tenant(pool : & PgPool, tenant_id : Uuid,) -> Result <Vec <Party>, PartyError> |
async fn list_parties_by_type_for_tenant(pool : & PgPool, tenant_id : Uuid, party_type : PartyType,) -> Result <Vec <Party>, PartyError> |
async fn list_persons_for_tenant(pool : & PgPool, tenant_id : Uuid,) -> Result <Vec <Person>, PartyError> |
async fn list_organizations_by_type(pool : & PgPool, org_type : OrganizationType,) -> Result <Vec <Organization>, PartyError> |
async fn list_organizations_for_tenant(pool : & PgPool, tenant_id : Uuid,) -> Result <Vec <Organization>, PartyError> |
async fn list_relationships_for_party_for_tenant(pool : & PgPool, tenant_id : Uuid, party_id : Uuid, limit : i64, offset : i64,) -> Result <(Vec <PartyRelationship>, i64), PartyError> |
async fn list_outgoing_relationships(pool : & PgPool, party_id : PartyId,) -> Result <Vec <PartyRelationship>, PartyError> |
async fn list_incoming_relationships(pool : & PgPool, party_id : PartyId,) -> Result <Vec <PartyRelationship>, PartyError> |
async fn list_relationships_between(pool : & PgPool, party_a : PartyId, party_b : PartyId,) -> Result <Vec <PartyRelationship>, PartyError> |
async fn list_animals(pool : & PgPool) -> Result <Vec <Animal>, PartyError> |
async fn list_animals_by_species(pool : & PgPool, species : Species,) -> Result <Vec <Animal>, PartyError> |
verb:parse
Repository functions for party models.
| Item |
|---|
fn parse_org_type(s : & str) -> OrganizationType |
fn parse_relationship_type(s : & str) -> RelationshipType |
verb:person
Repository functions for party models.
| Item |
|---|
fn person_search_pattern(needle : & str) -> Option <String> |
verb:relationship
Repository functions for party models.
| Item |
|---|
async fn relationship_exists(pool : & PgPool, from_party_id : PartyId, to_party_id : PartyId, relationship_type : RelationshipType,) -> Result <bool, PartyError> |
verb:search
Repository functions for party models.
| Item |
|---|
async fn search_persons_page_for_tenant(pool : & PgPool, tenant_id : Uuid, search : Option <& str>, limit : i64, offset : i64,) -> Result <(Vec <Person>, i64), PartyError> |
async fn search_organizations_page_for_tenant(pool : & PgPool, tenant_id : Uuid, search : Option <& str>, limit : i64, offset : i64,) -> Result <(Vec <Organization>, i64), PartyError> |
verb:soft
Repository functions for party models.
| Item |
|---|
async fn soft_delete_party(pool : & PgPool, id : PartyId) -> Result <(), PartyError> |
async fn soft_delete_person_for_tenant(pool : & PgPool, tenant_id : Uuid, id : Uuid,) -> Result <(), PartyError> |
async fn soft_delete_organization_for_tenant(pool : & PgPool, tenant_id : Uuid, id : Uuid,) -> Result <(), PartyError> |
async fn soft_delete_relationship_for_tenant(pool : & PgPool, tenant_id : Uuid, id : Uuid,) -> Result <(), PartyError> |
async fn soft_delete_animal(pool : & PgPool, id : Uuid) -> Result <(), PartyError> |
verb:update
Repository functions for party models.
| Item |
|---|
async fn update_party(pool : & PgPool, id : PartyId, display_name : & str,) -> Result <Option <Party>, PartyError> |
async fn update_person_for_tenant(pool : & PgPool, tenant_id : Uuid, person : & Person,) -> Result <Option <Person>, PartyError> |
async fn update_organization_for_tenant(pool : & PgPool, tenant_id : Uuid, org : & Organization,) -> Result <Option <Organization>, PartyError> |
async fn update_relationship_for_tenant(pool : & PgPool, tenant_id : Uuid, rel : & PartyRelationship,) -> Result <Option <PartyRelationship>, PartyError> |
How to use it
From `examples/party_cli.rs`:
use identity_parties::{
repo, Group, GroupType, Organization, OrganizationType, PartyId, PartyRelationship, Person,
};
use sqlx::postgres::PgPoolOptions;
use sqlx::PgPool;
use std::io::{self, Write};
use uuid::Uuid;
const DEMO_TENANT_ID: Uuid = Uuid::nil();
#[tokio::main]
async fn main() {
println!("╔═══════════════════════════════════════════╗");
println!("║ Party Management CLI ║");
println!("╚═══════════════════════════════════════════╝");
println!();
From `examples/seed_demo_crm.rs`:
use chrono::{NaiveDate, TimeZone, Utc};
use identity_parties::{
repo, LeadSource, LeadStatus, Organization, OrganizationType, PartyId, PartyRelationship,
Person, RelationshipType,
};
use sqlx::postgres::PgPoolOptions;
use sqlx::PgPool;
use uuid::Uuid;
const DEFAULT_TENANT: &str = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa";
mod ids {
pub const MARCUS: &str = "d1000000-0000-4000-8000-000000000001";
pub const ELENA: &str = "d1000000-0000-4000-8000-000000000002";
pub const PRIYA: &str = "d1000000-0000-4000-8000-000000000003";
pub const TOM: &str = "d1000000-0000-4000-8000-000000000004";
From this crate's own rustdoc:
use identity_parties::{
Person, Organization, Group, PartyRelationship, PartyId,
OrganizationType, GroupType, RelationshipType,
};
// Create a person
let person = Person::new("John")
.with_last_name("Doe")
.with_email("john@example.com");
// Create an organization
let org = Organization::new("Acme Corp")
.with_type(OrganizationType::Company);
// Create a relationship (using PartyId for type-safe party references)
let employment = PartyRelationship::employee(
PartyId::new(person.id),
PartyId::new(org.id),
).with_title("Software Engineer");
Module structure
identity_parties
authauth::rolesauth::tiersrepo
flowchart TD n_identity_parties["identity_parties"] n_identity_parties --> n_auth["auth"] n_auth --> n_auth__roles["roles"] n_auth --> n_auth__tiers["tiers"] n_identity_parties --> n_repo["repo"]
Public surface
`auth`
| Item | What it is |
|---|---|
pub struct PartyAuthService | Service for party-based authorization checks. |
pub struct EffectiveAccess | User's effective access combining permission tier and business roles. |
PartyAuthService :: fn new(pool : PgPool) -> Self | Create a new party auth service. |
PartyAuthService :: async fn get_party_id_for_user(& self, user_id : Uuid) -> Result <Option <Uuid>, PartyError> | Get the party_id for a user |
PartyAuthService :: async fn user_has_business_role(& self, user_id : Uuid, role_code : & str,) -> Result <bool, PartyError> | Check if a user has a specific business role via their party. |
PartyAuthService :: async fn get_user_business_roles(& self, user_id : Uuid) -> Result <Vec <String>, PartyError> | Get all business roles for a user. |
PartyAuthService :: async fn party_has_role(& self, party_id : Uuid, role_code : & str,) -> Result <bool, PartyError> | Check if a party has a specific business role. |
PartyAuthService :: async fn get_effective_access(& self, user_id : Uuid,) -> Result <Option <EffectiveAccess>, PartyError> | Get a user's effective access (permission tier + business roles). |
PartyAuthService :: async fn assign_party_role(& self, party_id : Uuid, role_code : & str, notes : Option <& str>,) -> Result <Uuid, PartyError> | Assign a business role to a party |
PartyAuthService :: async fn remove_party_role(& self, party_id : Uuid, role_code : & str,) -> Result <bool, PartyError> | Remove a business role from a party |
PartyAuthService :: async fn create_user_for_party(& self, party_id : Uuid, email : & str, password_hash : & str, permission_tier : & str,) -> Result <Uuid, PartyError> | Create a user account for an existing party |
PartyAuthService :: async fn require_business_role(& self, user_id : Uuid, role_code : & str,) -> Result <Uuid, PartyError> | Require that a user has a specific business role |
`auth::roles`
| Item | What it is |
|---|---|
pub const CUSTOMER: & str | Customer role - can place orders, has customer portal access |
pub const VENDOR: & str | Vendor role - supplies goods or services |
pub const EMPLOYEE: & str | Employee role - works at the business |
pub const INSPECTOR: & str | Inspector role - certified to perform inspections |
pub const MANUFACTURER: & str | Manufacturer role - produces equipment or goods |
`auth::tiers`
| Item | What it is |
|---|---|
pub const ADMIN: & str | Admin tier - full access to all features |
pub const OPERATOR: & str | Operator tier - standard operational access |
pub const VIEWER: & str | Viewer tier - read-only access |
`repo`
| Item | What it is |
|---|---|
async fn create_party(pool : & PgPool, party : & Party) -> Result <Party, PartyError> | Create a new party in the database |
async fn find_party_by_id_for_tenant(pool : & PgPool, tenant_id : Uuid, id : PartyId,) -> Result <Option <Party>, PartyError> | Find a party by its ID for a specific tenant |
async fn list_parties_for_tenant(pool : & PgPool, tenant_id : Uuid,) -> Result <Vec <Party>, PartyError> | List all active (non-deleted) parties for a specific tenant |
async fn list_parties_by_type_for_tenant(pool : & PgPool, tenant_id : Uuid, party_type : PartyType,) -> Result <Vec <Party>, PartyError> | List parties for a tenant filtered by party type |
async fn soft_delete_party(pool : & PgPool, id : PartyId) -> Result <(), PartyError> | Soft delete a party by setting deleted_at |
async fn update_party(pool : & PgPool, id : PartyId, display_name : & str,) -> Result <Option <Party>, PartyError> | Update a party's display_name (stored in the parties.name column) |
async fn create_person_for_tenant(pool : & PgPool, tenant_id : Uuid, person : & Person,) -> Result <Person, PartyError> | Create a person with a corresponding Party record for a tenant |
async fn find_person_by_id_for_tenant(pool : & PgPool, tenant_id : Uuid, id : Uuid,) -> Result <Option <Person>, PartyError> | Find a person by their ID for a specific tenant |
async fn find_person_by_email_for_tenant(pool : & PgPool, tenant_id : Uuid, email : & str,) -> Result <Option <Person>, PartyError> | Find a person by email for a specific tenant |
async fn list_persons_for_tenant(pool : & PgPool, tenant_id : Uuid,) -> Result <Vec <Person>, PartyError> | List all active persons for a specific tenant |
pub const PERSON_SEARCH_COLUMNS: & & str | The person columns a search= term is matched against — the same fields the list view displays, and the same list search_persons_page_for_tenant filters on |
fn person_search_pattern(needle : & str) -> Option <String> | Turn a user-supplied search term into a safe ILIKE pattern |
async fn search_persons_page_for_tenant(pool : & PgPool, tenant_id : Uuid, search : Option <& str>, limit : i64, offset : i64,) -> Result <(Vec <Person>, i64), PartyError> | One page of a tenant's persons, plus how many the filter matches in total |
async fn soft_delete_person_for_tenant(pool : & PgPool, tenant_id : Uuid, id : Uuid,) -> Result <(), PartyError> | Soft delete a person for a specific tenant (deletes both party and person records) |
async fn update_person_for_tenant(pool : & PgPool, tenant_id : Uuid, person : & Person,) -> Result <Option <Person>, PartyError> | Update a person for a specific tenant, returning the stored row |
async fn list_organizations_by_type(pool : & PgPool, org_type : OrganizationType,) -> Result <Vec <Organization>, PartyError> | List organizations by type |
async fn create_organization_for_tenant(pool : & PgPool, tenant_id : Uuid, org : & Organization,) -> Result <Organization, PartyError> | Create an organization with a corresponding Party record for a tenant |
async fn find_organization_by_id_for_tenant(pool : & PgPool, tenant_id : Uuid, id : Uuid,) -> Result <Option <Organization>, PartyError> | Find an organization by its ID for a specific tenant |
async fn list_organizations_for_tenant(pool : & PgPool, tenant_id : Uuid,) -> Result <Vec <Organization>, PartyError> | List all active organizations for a specific tenant |
pub const ORGANIZATION_SEARCH_COLUMNS: & & str | The organization columns a search= term is matched against — mirrors PERSON_SEARCH_COLUMNS. |
async fn search_organizations_page_for_tenant(pool : & PgPool, tenant_id : Uuid, search : Option <& str>, limit : i64, offset : i64,) -> Result <(Vec <Organization>, i64), PartyError> | One page of a tenant's organizations, plus how many the filter matches in total |
async fn soft_delete_organization_for_tenant(pool : & PgPool, tenant_id : Uuid, id : Uuid,) -> Result <(), PartyError> | Soft delete an organization for a specific tenant (deletes both the party and organization records) |
async fn update_organization_for_tenant(pool : & PgPool, tenant_id : Uuid, org : & Organization,) -> Result <Option <Organization>, PartyError> | Update an organization for a specific tenant, returning the stored row |
fn parse_org_type(s : & str) -> OrganizationType | Parse the database string representation of an OrganizationType back into the enum |
async fn create_group(pool : & PgPool, group : & Group) -> Result <Group, PartyError> | Create a new group in the database |
async fn find_group_by_id(pool : & PgPool, id : Uuid) -> Result <Option <Group>, PartyError> | Find a group by its ID |
async fn create_relationship_for_tenant(pool : & PgPool, tenant_id : Uuid, rel : & PartyRelationship,) -> Result <PartyRelationship, PartyError> | Create a relationship for a tenant |
async fn find_relationship_by_id_for_tenant(pool : & PgPool, tenant_id : Uuid, id : Uuid,) -> Result <Option <PartyRelationship>, PartyError> | Find a relationship by id for a specific tenant |
async fn list_relationships_for_party_for_tenant(pool : & PgPool, tenant_id : Uuid, party_id : Uuid, limit : i64, offset : i64,) -> Result <(Vec <PartyRelationship>, i64), PartyError> | One page of the relationships involving party_id (either direction), for a specific tenant, plus how many match in total |
async fn update_relationship_for_tenant(pool : & PgPool, tenant_id : Uuid, rel : & PartyRelationship,) -> Result <Option <PartyRelationship>, PartyError> | Update a relationship for a specific tenant, returning the stored row |
async fn soft_delete_relationship_for_tenant(pool : & PgPool, tenant_id : Uuid, id : Uuid,) -> Result <(), PartyError> | Soft delete a relationship for a specific tenant |
async fn find_relationships_for_party(pool : & PgPool, party_id : PartyId,) -> Result <Vec <PartyRelationship>, PartyError> | Find all relationships where a party is the from_party |
async fn find_relationships_for_person(pool : & PgPool, person_id : Uuid,) -> Result <Vec <PartyRelationship>, PartyError> | Find all relationships where a person is the from_party |
async fn find_relationships_involving_party(pool : & PgPool, party_id : PartyId,) -> Result <Vec <PartyRelationship>, PartyError> | Find all relationships involving a party (as either from or to) |
async fn find_relationships_for_animal(pool : & PgPool, animal_id : Uuid,) -> Result <Vec <PartyRelationship>, PartyError> | Find all relationships involving an animal (as either from or to) |
fn parse_relationship_type(s : & str) -> RelationshipType | Parse the database string representation of a RelationshipType back into the enum |
async fn list_outgoing_relationships(pool : & PgPool, party_id : PartyId,) -> Result <Vec <PartyRelationship>, PartyError> | List all outgoing relationships for a party |
async fn list_incoming_relationships(pool : & PgPool, party_id : PartyId,) -> Result <Vec <PartyRelationship>, PartyError> | List all incoming relationships for a party |
async fn list_relationships_between(pool : & PgPool, party_a : PartyId, party_b : PartyId,) -> Result <Vec <PartyRelationship>, PartyError> | List all relationships between two parties (in either direction) |
async fn relationship_exists(pool : & PgPool, from_party_id : PartyId, to_party_id : PartyId, relationship_type : RelationshipType,) -> Result <bool, PartyError> | Check if a specific relationship exists |
async fn find_relationship(pool : & PgPool, from_party_id : PartyId, to_party_id : PartyId, relationship_type : RelationshipType,) -> Result <Option <PartyRelationship>, PartyError> | Find a specific relationship by its parties and type |
async fn create_animal(pool : & PgPool, animal : & Animal) -> Result <Animal, PartyError> | Create a new animal in the database |
async fn find_animal_by_id(pool : & PgPool, id : Uuid) -> Result <Option <Animal>, PartyError> | Find an animal by its ID |
async fn list_animals(pool : & PgPool) -> Result <Vec <Animal>, PartyError> | List all active (non-deleted) animals |
async fn list_animals_by_species(pool : & PgPool, species : Species,) -> Result <Vec <Animal>, PartyError> | List animals by species |
async fn soft_delete_animal(pool : & PgPool, id : Uuid) -> Result <(), PartyError> | Soft delete an animal by setting deleted_at |
Re-exports. Exported here, defined elsewhere.
| Export | Defined in |
|---|---|
Group | group::Group |
Organization | organization::Organization |
PartyError | error::PartyError |
PartyRelationship | relationship::PartyRelationship |
Person | person::Person |
{AddressType,EmailType,FromParty,Gender,GroupType,LeadSource,LeadStatus,MaritalStatus,OrganizationType,PartyRef,PhoneType,RelationshipType,ToParty,} | enums::{AddressType,EmailType,FromParty,Gender,GroupType,LeadSource,LeadStatus,MaritalStatus,OrganizationType,PartyRef,PhoneType,RelationshipType,ToParty,} |
{Animal,AnimalSex,Species} | animal::{Animal,AnimalSex,Species} |
{BillingProfile,FiscalAddress,InvoiceDelivery,PaymentMethod,PaymentTerms,TaxRegistration,} | billing::{BillingProfile,FiscalAddress,InvoiceDelivery,PaymentMethod,PaymentTerms,TaxRegistration,} |
{HasId,SoftDeletable,Timestamped} | foundation_basemodels::{HasId,SoftDeletable,Timestamped} |
{Party,PartyId,PartyType} | party::{Party,PartyId,PartyType} |
{roles,tiers,EffectiveAccess,PartyAuthService} | auth::{roles,tiers,EffectiveAccess,PartyAuthService} |
Boundary
Reaches into foundation.
Shares tier identity with 7 other crates: identity-auth, identity-identifiers, identity-impersonation, identity-party-places, identity-rbac, identity-tenant, identity-user-prefs.
_What this crate deliberately does NOT own is a judgment. No committed registry records one for it, so none is stated here._
Where it sits
| Tier (ontology) | identity |
| Architectural role (taxonomy) | unclassified (baselined) |
| Location | crates/identity/parties |
| Vocabulary in force (lexicon) | current |
Tier flow. Which tiers this crate's own edges cross.
flowchart LR n_identity["identity"] --> n_foundation["foundation"]
Dependencies
Runtime, in this workspace.
| Crate | Tier | Optional | Only on |
|---|---|---|---|
| `foundation-basemodels` | foundation | no | always |
Runtime, from outside the workspace.
| Crate | Requirement | Features | Optional | Only on |
|---|---|---|---|---|
chrono | ^0.4 | serde | no | always |
rust_decimal | ^1 | serde | no | always |
serde | ^1 | derive | no | always |
serde_json | ^1 | — | no | always |
sqlx | ^0.8 | runtime-tokio, postgres, chrono, uuid, json | no | always |
thiserror | ^2 | — | no | always |
tokio | ^1 | full | no | always |
uuid | ^1 | v4, v7, serde, js | no | always |
Development, from outside the workspace.
| Crate | Requirement | Features | Optional | Only on |
|---|---|---|---|---|
dotenvy | ^0.15 | — | no | always |
rust_decimal_macros | ^1 | — | no | always |
tokio-test | ^0.4 | — | no | always |
Build. None.
Depended on by. 21 workspace crates.
Signal flow — what reaches this crate, and what it reaches.
flowchart LR n_application_calendar["application-calendar"] -->|uses| SELF n_application_conversation["application-conversation"] -->|uses| SELF n_application_parties["application-parties"] -->|uses| SELF n_application_party_api["application-party-api"] -->|uses| SELF n_application_party_discovery["application-party-discovery"] -->|uses| SELF n_content_assets["content-assets"] -->|uses| SELF n_content_calendar["content-calendar"] -->|uses| SELF n_content_calendar_recurrence["content-calendar-recurrence"] -->|uses| SELF n_domain_billing["domain-billing"] -->|uses| SELF n_domain_catalog["domain-catalog"] -->|uses| SELF n_domain_encounters["domain-encounters"] -->|uses| SELF n_domain_legal_evidence["domain-legal-evidence"] -->|uses| SELF n_domain_legal_matter["domain-legal-matter"] -->|uses| SELF n_domain_legal_procedure["domain-legal-procedure"] -->|uses| SELF n_domain_travel_compliance["domain-travel-compliance"] -->|uses| SELF n_domain_veterinary["domain-veterinary"] -->|uses| SELF n_foundation_test_support["foundation-test-support"] -->|uses| SELF n_identity_auth["identity-auth"] -->|uses| SELF n_identity_identifiers["identity-identifiers"] -->|uses| SELF n_infrastructure_communication["infrastructure-communication"] -->|uses| SELF n_operations_planning["operations-planning"] -->|uses| SELF SELF["identity-parties"] SELF -->|runtime| n_foundation_basemodels["foundation-basemodels"] classDef self fill:#1f883d,stroke:#1f883d,color:#fff; class SELF self;
Feature flags
| Feature | Enables | On by default |
|---|---|---|
postgres | — | no |
flowchart LR n_postgres["postgres"]
Targets
| Kind | Name | Source |
|---|---|---|
| example | party-cli | `examples/party_cli.rs` |
| example | seed_demo_crm | `examples/seed_demo_crm.rs` |
| lib | identity_parties | `src/lib.rs` |
| test | integration_tests | `tests/integration_tests.rs` |
Error model
No public error type was detected: no public item declares a type named *Error, and no public signature returns one.
Operational characteristics
| Property | Evidence |
|---|---|
| async public surface | yes |
| async runtime | yes |
| database access | yes |
| network I/O | none detected |
| unsafe code | none detected |
| environment variables | yes |
No unsafe block, unsafe fn, unsafe impl or unsafe trait was found by the parser anywhere in this crate's source.
Configuration
| Variable | Read in |
|---|---|
DATABASE_URL | src/repo.rs |
TEST_DATABASE_URL | src/repo.rs |
Related capabilities
21 workspace crates depend on this one: application-calendar, application-conversation, application-parties, application-party-api, application-party-discovery, content-assets, content-calendar, content-calendar-recurrence, domain-billing, domain-catalog, domain-encounters, domain-legal-evidence, … (21 total).
Verification
| Kind | Count |
|---|---|
| Unit tests | 112 |
| Integration tests | 17 |
| Examples | 2 |
| Doctests | 1 |
Evidence by module. How often each public module is named by something executable.
| Module | Tests | Examples | Consumers |
|---|---|---|---|
auth | 2 | 0 | 0 |
auth::roles | 5 | 0 | 0 |
auth::tiers | 3 | 0 | 1 |
repo | 46 | 8 | 37 |
What the tests establish, by name:
a_caller_probing_another_tenants_party_id_gets_party_not_found—tests/integration_tests.rssame_tenant_relationship_is_still_returned—tests/integration_tests.rstest_create_employee_relationship—tests/integration_tests.rstest_create_group_with_primary_contact—tests/integration_tests.rstest_create_group_with_valid_data_succeeds—tests/integration_tests.rstest_create_organization_with_valid_data_succeeds—tests/integration_tests.rstest_create_person_with_valid_data_succeeds—tests/integration_tests.rstest_find_group_by_id_returns_existing—tests/integration_tests.rstest_find_organization_by_id_returns_existing—tests/integration_tests.rstest_find_person_by_email_returns_person—tests/integration_tests.rstest_find_person_by_id_returns_existing_person—tests/integration_tests.rstest_find_person_by_id_returns_none_for_missing—tests/integration_tests.rstest_find_relationships_for_person—tests/integration_tests.rstest_list_organizations_by_type—tests/integration_tests.rstest_list_persons_returns_all_active—tests/integration_tests.rstest_self_relationship_rejected—tests/integration_tests.rstest_soft_delete_person_excludes_from_list—tests/integration_tests.rstest_animal_builder_pattern—src/animal.rstest_animal_sex_parse—src/animal.rstest_new_animal_has_default_values—src/animal.rstest_species_parse—src/animal.rstest_billing_profile_available_credit—src/billing.rstest_billing_profile_calculate_due_date—src/billing.rstest_billing_profile_has_id—src/billing.rstest_billing_profile_new—src/billing.rstest_billing_profile_serialization—src/billing.rstest_billing_profile_soft_delete—src/billing.rstest_billing_profile_with_credit_limit—src/billing.rstest_billing_profile_with_credit_limit_dollars—src/billing.rstest_billing_profile_with_currency—src/billing.rs- _… 99 more_
Documentation coverage
| Measure | Documented | Total |
|---|---|---|
| Public items with rustdoc | 66 | 66 |
Public modules with a //! block | 2 | 4 |
pie showData
title Public items with rustdoc
"Documented" : 66
"No rustdoc detected" : 0
Metrics
| Metric | Value |
|---|---|
| Rust source files | 12 |
| Source lines | 6203 |
| Code lines | 4406 |
| Public API items | 66 |
| Public modules | 4 |
| Tests | 129 |
| Examples | 2 |
| Cargo features | 1 |
| Direct runtime dependencies | 9 |
| Workspace reverse dependencies | 21 |
pie showData
title Public API by kind
"constant" : 10
"function" : 44
"method" : 10
"struct" : 2
pie showData
title Rust source composition
"Code" : 4406
"Blank or comment" : 1797
Generation
Rendered by tools-corpus corpus readme from repository evidence alone, renderer schema 2, lexicon current. No model, network service or database was consulted. Regenerate with tools-corpus corpus readme --write; verify with --check.