Link between parties (orgs, persons) and places (physical locations) with roles
| Tier | identity |
| Role | unclassified (baselined) |
| Path | crates/identity/party-places |
| Edition | 2021 |
| Targets | identity_party_places |
| Public items | 51 across 1 module |
| Tests | 26 |
What it is for
# identity-party-places
Link between parties (organizations, persons, groups) and places (physical locations).
Design Philosophy
A PartyPlace answers: "What role does this place play for this party?"
This enables:
- One organization with multiple locations (branches, warehouses, HQ)
- Each location can have its own contacts, identifiers, and metadata
- Role-based semantics (headquarters vs branch vs warehouse)
Key Relationships
Organization (Party)
│
├── PartyPlace (role=headquarters, is_primary=true)
│ └── Place (Phoenix HQ)
│ ├── Contact (local phone)
│ └── Identifier (google_place_id)
│
├── PartyPlace (role=branch)
│ └── Place (Surprise Branch)
│ ├── Contact (branch phone)
│ └── Identifier (google_place_id)
│
└── PartyPlace (role=branch)
└── Place (Sun City Branch)
├── Contact (branch phone)
└── Identifier (google_place_id)
Example
use identity_party_places::{PartyPlace, PartyType, PlaceRole};
use uuid::Uuid;
// Link an organization to its headquarters
let hq_link = PartyPlace::new(
Uuid::new_v4(), // tenant_id
PartyType::Organization,
Uuid::new_v4(), // org_id
Uuid::new_v4(), // place_id (Phoenix HQ)
)
.with_role(PlaceRole::Headquarters)
.as_primary();
// Link same org to a branch
let branch_link = PartyPlace::new(
hq_link.tenant_id,
PartyType::Organization,
hq_link.party_id,
Uuid::new_v4(), // place_id (Surprise Branch)
)
.with_role(PlaceRole::Branch)
.with_label("Surprise Location");
Capabilities
PartyPlace:PartyPlace
# identity-party-places
| Item |
|---|
pub struct PartyPlace |
PartyPlace:as
# identity-party-places
| Item |
|---|
PartyPlace :: fn as_primary(mut self) -> Self |
PartyPlace:branch
# identity-party-places
| Item |
|---|
PartyPlace :: fn branch(tenant_id : Uuid, party_type : PartyType, party_id : Uuid, place_id : Uuid) -> Self |
PartyPlace:builder
# identity-party-places
| Item |
|---|
PartyPlace :: fn builder() -> PartyPlaceBuilder |
PartyPlace:created
# identity-party-places
| Item |
|---|
PartyPlace :: fn created_at(& self) -> DateTime <Utc> |
PartyPlace:deactivate
# identity-party-places
| Item |
|---|
PartyPlace :: fn deactivate(mut self) -> Self |
PartyPlace:deleted
# identity-party-places
| Item |
|---|
PartyPlace :: fn deleted_at(& self) -> Option <DateTime <Utc>> |
PartyPlace:display
# identity-party-places
| Item |
|---|
PartyPlace :: fn display_name(& self) -> String |
PartyPlace:headquarters
# identity-party-places
| Item |
|---|
PartyPlace :: fn headquarters(tenant_id : Uuid, party_type : PartyType, party_id : Uuid, place_id : Uuid,) -> Self |
PartyPlace:home
# identity-party-places
| Item |
|---|
PartyPlace :: fn home(tenant_id : Uuid, party_id : Uuid, place_id : Uuid) -> Self |
PartyPlace:id
# identity-party-places
| Item |
|---|
PartyPlace :: fn id(& self) -> Uuid |
PartyPlace:is
# identity-party-places
| Item |
|---|
PartyPlace :: fn is_headquarters(& self) -> bool |
PartyPlace :: fn is_branch(& self) -> bool |
PartyPlace :: fn is_effective(& self) -> bool |
PartyPlace:new
# identity-party-places
| Item |
|---|
PartyPlace :: fn new(tenant_id : Uuid, party_type : PartyType, party_id : Uuid, place_id : Uuid) -> Self |
PartyPlace:updated
# identity-party-places
| Item |
|---|
PartyPlace :: fn updated_at(& self) -> DateTime <Utc> |
PartyPlace:with
# identity-party-places
| Item |
|---|
PartyPlace :: fn with_role(mut self, role : PlaceRole) -> Self |
PartyPlace :: fn with_label(mut self, label : impl Into <String>) -> Self |
PartyPlace :: fn with_notes(mut self, notes : impl Into <String>) -> Self |
PartyPlace :: fn with_effective_dates(mut self, from : Option <DateTime <Utc>>, to : Option <DateTime <Utc>>,) -> Self |
PartyPlace:work
# identity-party-places
| Item |
|---|
PartyPlace :: fn work(tenant_id : Uuid, party_id : Uuid, place_id : Uuid) -> Self |
PartyPlaceBuilder:PartyPlaceBuilder
# identity-party-places
| Item |
|---|
pub struct PartyPlaceBuilder |
PartyPlaceBuilder:build
# identity-party-places
| Item |
|---|
PartyPlaceBuilder :: fn build(self) -> Result <PartyPlace, PartyPlaceError> |
PartyPlaceBuilder:effective
# identity-party-places
| Item |
|---|
PartyPlaceBuilder :: fn effective_from(mut self, from : DateTime <Utc>) -> Self |
PartyPlaceBuilder :: fn effective_to(mut self, to : DateTime <Utc>) -> Self |
PartyPlaceBuilder:is
# identity-party-places
| Item |
|---|
PartyPlaceBuilder :: fn is_primary(mut self, is_primary : bool) -> Self |
PartyPlaceBuilder :: fn is_active(mut self, is_active : bool) -> Self |
PartyPlaceBuilder:label
# identity-party-places
| Item |
|---|
PartyPlaceBuilder :: fn label(mut self, label : impl Into <String>) -> Self |
PartyPlaceBuilder:new
# identity-party-places
| Item |
|---|
PartyPlaceBuilder :: fn new() -> Self |
PartyPlaceBuilder:notes
# identity-party-places
| Item |
|---|
PartyPlaceBuilder :: fn notes(mut self, notes : impl Into <String>) -> Self |
PartyPlaceBuilder:party
# identity-party-places
| Item |
|---|
PartyPlaceBuilder :: fn party_type(mut self, party_type : PartyType) -> Self |
PartyPlaceBuilder :: fn party_id(mut self, party_id : Uuid) -> Self |
PartyPlaceBuilder:place
# identity-party-places
| Item |
|---|
PartyPlaceBuilder :: fn place_id(mut self, place_id : Uuid) -> Self |
PartyPlaceBuilder:role
# identity-party-places
| Item |
|---|
PartyPlaceBuilder :: fn role(mut self, role : PlaceRole) -> Self |
PartyPlaceBuilder:tenant
# identity-party-places
| Item |
|---|
PartyPlaceBuilder :: fn tenant_id(mut self, tenant_id : Uuid) -> Self |
PartyPlaceError
# identity-party-places
| Item |
|---|
pub enum PartyPlaceError |
PartyType
# identity-party-places
| Item |
|---|
pub enum PartyType |
PartyType :: fn fmt(& self, f : & mut std::fmt::Formatter <'_>) -> std::fmt::Result |
PartyType :: fn from_str(s : & str) -> Result <Self, Self::Err> |
PlaceRole
# identity-party-places
| Item |
|---|
pub enum PlaceRole |
PlaceRole :: fn fmt(& self, f : & mut std::fmt::Formatter <'_>) -> std::fmt::Result |
PlaceRole :: fn from_str(s : & str) -> Result <Self, Self::Err> |
repo (other)
Tenant-scoped repository for the composed "party address" surface —
| Item |
|---|
async fn create_party_address_for_tenant(pool : & PgPool, tenant_id : Uuid, party_type : PartyType, party_id : Uuid, role : PlaceRole, label : Option <& str>, is_primary : bool, street1 : & str, street2 : Option <& str>, city : & str, state : Option <& str>, postal_code : Option <& str>, country : & str,) -> Result <PartyAddress, PartyAddressRepoError> |
async fn find_party_address_by_id_for_tenant(pool : & PgPool, tenant_id : Uuid, id : Uuid,) -> Result <Option <PartyAddress>, PartyAddressRepoError> |
async fn list_party_addresses_for_party_for_tenant(pool : & PgPool, tenant_id : Uuid, party_id : Uuid, role : Option <PlaceRole>,) -> Result <Vec <PartyAddress>, PartyAddressRepoError> |
async fn update_party_address_for_tenant(pool : & PgPool, tenant_id : Uuid, id : Uuid, role : PlaceRole, label : Option <& str>, is_active : bool, street1 : & str, street2 : Option <& str>, city : & str, state : Option <& str>, postal_code : Option <& str>, country : & str,) -> Result <Option <PartyAddress>, PartyAddressRepoError> |
async fn set_primary_for_tenant(pool : & PgPool, tenant_id : Uuid, id : Uuid,) -> Result <Option <PartyAddress>, PartyAddressRepoError> |
PartyAddress
Tenant-scoped repository for the composed "party address" surface —
| Item |
|---|
pub struct PartyAddress |
PartyAddressRepoError
Tenant-scoped repository for the composed "party address" surface —
| Item |
|---|
pub enum PartyAddressRepoError |
PartyAddressRepoError :: fn from(e : GeoRepoError) -> Self |
async fn soft_delete_party_address_for_tenant(pool : & PgPool, tenant_id : Uuid, id : Uuid,) -> Result <(), PartyAddressRepoError> |
How to use it
From this crate's own rustdoc:
## Example
Module structure
identity_party_places
repo
Public surface
`crate root`
| Item | What it is |
|---|---|
pub enum PartyPlaceError | Party-place link errors. |
pub enum PartyType | Type of party in the link. |
PartyType :: fn fmt(& self, f : & mut std::fmt::Formatter <'_>) -> std::fmt::Result | — |
PartyType :: fn from_str(s : & str) -> Result <Self, Self::Err> | — |
pub enum PlaceRole | Role/purpose of the place for the party. |
PlaceRole :: fn fmt(& self, f : & mut std::fmt::Formatter <'_>) -> std::fmt::Result | — |
PlaceRole :: fn from_str(s : & str) -> Result <Self, Self::Err> | — |
pub struct PartyPlace | A link between a party and a place with role semantics |
PartyPlace :: fn new(tenant_id : Uuid, party_type : PartyType, party_id : Uuid, place_id : Uuid) -> Self | Create a new party-place link. |
PartyPlace :: fn builder() -> PartyPlaceBuilder | Create a new builder. |
PartyPlace :: fn with_role(mut self, role : PlaceRole) -> Self | Set the role. |
PartyPlace :: fn with_label(mut self, label : impl Into <String>) -> Self | Set the label. |
PartyPlace :: fn as_primary(mut self) -> Self | Mark as primary. |
PartyPlace :: fn with_notes(mut self, notes : impl Into <String>) -> Self | Set notes. |
PartyPlace :: fn with_effective_dates(mut self, from : Option <DateTime <Utc>>, to : Option <DateTime <Utc>>,) -> Self | Set effective dates. |
PartyPlace :: fn deactivate(mut self) -> Self | Deactivate this link. |
PartyPlace :: fn is_headquarters(& self) -> bool | Check if this is the headquarters. |
PartyPlace :: fn is_branch(& self) -> bool | Check if this is a branch location. |
PartyPlace :: fn is_effective(& self) -> bool | Check if this link is currently effective. |
PartyPlace :: fn display_name(& self) -> String | Get a display-friendly name. |
PartyPlace :: fn id(& self) -> Uuid | — |
PartyPlace :: fn created_at(& self) -> DateTime <Utc> | — |
PartyPlace :: fn updated_at(& self) -> DateTime <Utc> | — |
PartyPlace :: fn deleted_at(& self) -> Option <DateTime <Utc>> | — |
pub struct PartyPlaceBuilder | Builder for PartyPlace. |
PartyPlaceBuilder :: fn new() -> Self | Create a new builder. |
PartyPlaceBuilder :: fn tenant_id(mut self, tenant_id : Uuid) -> Self | Set the tenant ID. |
PartyPlaceBuilder :: fn party_type(mut self, party_type : PartyType) -> Self | Set the party type. |
PartyPlaceBuilder :: fn party_id(mut self, party_id : Uuid) -> Self | Set the party ID. |
PartyPlaceBuilder :: fn place_id(mut self, place_id : Uuid) -> Self | Set the place ID. |
PartyPlaceBuilder :: fn role(mut self, role : PlaceRole) -> Self | Set the role. |
PartyPlaceBuilder :: fn label(mut self, label : impl Into <String>) -> Self | Set the label. |
PartyPlaceBuilder :: fn is_primary(mut self, is_primary : bool) -> Self | Set as primary. |
PartyPlaceBuilder :: fn is_active(mut self, is_active : bool) -> Self | Set as active. |
PartyPlaceBuilder :: fn effective_from(mut self, from : DateTime <Utc>) -> Self | Set effective from date. |
PartyPlaceBuilder :: fn effective_to(mut self, to : DateTime <Utc>) -> Self | Set effective to date. |
PartyPlaceBuilder :: fn notes(mut self, notes : impl Into <String>) -> Self | Set notes. |
PartyPlaceBuilder :: fn build(self) -> Result <PartyPlace, PartyPlaceError> | Build the party-place link |
PartyPlace :: fn headquarters(tenant_id : Uuid, party_type : PartyType, party_id : Uuid, place_id : Uuid,) -> Self | Create a headquarters link. |
PartyPlace :: fn branch(tenant_id : Uuid, party_type : PartyType, party_id : Uuid, place_id : Uuid) -> Self | Create a branch link. |
PartyPlace :: fn home(tenant_id : Uuid, party_id : Uuid, place_id : Uuid) -> Self | Create a home address link (for persons). |
PartyPlace :: fn work(tenant_id : Uuid, party_id : Uuid, place_id : Uuid) -> Self | Create a work address link (for persons). |
`repo`
| Item | What it is |
|---|---|
pub enum PartyAddressRepoError | Errors from the repository layer |
PartyAddressRepoError :: fn from(e : GeoRepoError) -> Self | — |
pub struct PartyAddress | The flattened read/write shape the API exposes — one row that answers "what does this party call this address, and where is it" without the caller ever walking party_places -> geo_places -> geo_addresses (ADR 0037) |
async fn create_party_address_for_tenant(pool : & PgPool, tenant_id : Uuid, party_type : PartyType, party_id : Uuid, role : PlaceRole, label : Option <& str>, is_primary : bool, street1 : & str, street2 : Option <& str>, city : & str, state : Option <& str>, postal_code : Option <& str>, country : & str,) -> Result <PartyAddress, PartyAddressRepoError> | Create a party address: one geo_addresses row, one geo_places row (its own, never shared — ADR 0037 decision 2) and one party_places row, all in ONE transaction (AC1) |
async fn find_party_address_by_id_for_tenant(pool : & PgPool, tenant_id : Uuid, id : Uuid,) -> Result <Option <PartyAddress>, PartyAddressRepoError> | One party address by id, scoped to tenant_id |
async fn list_party_addresses_for_party_for_tenant(pool : & PgPool, tenant_id : Uuid, party_id : Uuid, role : Option <PlaceRole>,) -> Result <Vec <PartyAddress>, PartyAddressRepoError> | Every non-deleted address for one party, scoped to tenant_id, optionally filtered to one role |
async fn update_party_address_for_tenant(pool : & PgPool, tenant_id : Uuid, id : Uuid, role : PlaceRole, label : Option <& str>, is_active : bool, street1 : & str, street2 : Option <& str>, city : & str, state : Option <& str>, postal_code : Option <& str>, country : & str,) -> Result <Option <PartyAddress>, PartyAddressRepoError> | Update a party address: the linked geo_addresses row AND the party_places row's role/label/is_active, in ONE transaction |
async fn soft_delete_party_address_for_tenant(pool : & PgPool, tenant_id : Uuid, id : Uuid,) -> Result <(), PartyAddressRepoError> | Soft-delete (AC5) |
async fn set_primary_for_tenant(pool : & PgPool, tenant_id : Uuid, id : Uuid,) -> Result <Option <PartyAddress>, PartyAddressRepoError> | Demote every other primary address in the same (party_id, role) scope, then promote id, atomically (AC3) |
No pub use re-exports: every item above is declared in this crate.
Boundary
Reaches into domain, foundation.
Shares tier identity with 7 other crates: identity-auth, identity-identifiers, identity-impersonation, identity-parties, 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/party-places |
| Vocabulary in force (lexicon) | current |
Tier flow. Which tiers this crate's own edges cross.
flowchart LR n_identity["identity"] --> n_domain["domain"] n_identity["identity"] --> n_foundation["foundation"]
Dependencies
Runtime, in this workspace.
| Crate | Tier | Optional | Only on |
|---|---|---|---|
| `domain-geo` | domain | no | always |
| `foundation-basemodels` | foundation | no | always |
Runtime, from outside the workspace.
| Crate | Requirement | Features | Optional | Only on |
|---|---|---|---|---|
chrono | ^0.4 | 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 |
|---|---|---|---|---|
tokio-test | ^0.4 | — | no | always |
Build. None.
Depended on by. 1 workspace crate.
Signal flow — what reaches this crate, and what it reaches.
flowchart LR n_application_parties["application-parties"] -->|uses| SELF SELF["identity-party-places"] SELF -->|runtime| n_domain_geo["domain-geo"] SELF -->|runtime| n_foundation_basemodels["foundation-basemodels"] classDef self fill:#1f883d,stroke:#1f883d,color:#fff; class SELF self;
Feature flags
No Cargo features are defined: every capability is unconditional, so no consumer can receive a half-wired crate.
Targets
| Kind | Name | Source |
|---|---|---|
| lib | identity_party_places | `src/lib.rs` |
Error model
| Error type | Named by |
|---|---|
PartyAddressRepoError | create_party_address_for_tenant, find_party_address_by_id_for_tenant, list_party_addresses_for_party_for_tenant, set_primary_for_tenant, soft_delete_party_address_for_tenant, update_party_address_for_tenant |
PartyPlaceError | declared, no public signature returns it |
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 | none detected |
No unsafe block, unsafe fn, unsafe impl or unsafe trait was found by the parser anywhere in this crate's source.
Configuration
No environment variable is read with a literal name anywhere in this crate. A variable whose key is computed at run time cannot be listed here, and is not claimed to be absent.
Related capabilities
1 workspace crate depends on this one: application-parties.
Verification
| Kind | Count |
|---|---|
| Unit tests | 26 |
| Integration tests | 0 |
| Examples | 0 |
| Doctests | 1 |
Evidence by module. How often each public module is named by something executable.
| Module | Tests | Examples | Consumers |
|---|---|---|---|
crate root | 5 | 0 | 4 |
repo | 8 | 0 | 8 |
What the tests establish, by name:
test_billing_is_its_own_variant_not_mailing—src/lib.rstest_branch_constructor—src/lib.rstest_builder_full—src/lib.rstest_builder_missing_place—src/lib.rstest_builder_missing_tenant—src/lib.rstest_deactivate—src/lib.rstest_display_name_with_label—src/lib.rstest_display_name_without_label—src/lib.rstest_error_display—src/lib.rstest_has_id_trait—src/lib.rstest_headquarters_constructor—src/lib.rstest_home_constructor—src/lib.rstest_is_effective_active—src/lib.rstest_is_effective_deactivated—src/lib.rstest_new—src/lib.rstest_place_role_default—src/lib.rstest_place_role_display—src/lib.rstest_place_role_from_str—src/lib.rstest_place_role_serialization—src/lib.rstest_receiving_is_a_documented_alias_for_shipping_not_a_silent_one—src/lib.rstest_serialization—src/lib.rstest_soft_deletable_trait—src/lib.rstest_timestamped_trait—src/lib.rstest_with_label—src/lib.rstest_with_role—src/lib.rstest_work_constructor—src/lib.rs
Documentation coverage
| Measure | Documented | Total |
|---|---|---|
| Public items with rustdoc | 42 | 51 |
Public modules with a //! block | 1 | 1 |
pie showData
title Public items with rustdoc
"Documented" : 42
"No rustdoc detected" : 9
Metrics
| Metric | Value |
|---|---|
| Rust source files | 2 |
| Source lines | 1502 |
| Code lines | 980 |
| Public API items | 51 |
| Public modules | 1 |
| Tests | 26 |
| Examples | 0 |
| Cargo features | 0 |
| Direct runtime dependencies | 9 |
| Workspace reverse dependencies | 1 |
pie showData
title Public API by kind
"enum" : 4
"function" : 6
"method" : 38
"struct" : 3
pie showData
title Rust source composition
"Code" : 980
"Blank or comment" : 522
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.