Polymorphic Party Model

Person and Organization abstractions, unified address book, and contact point management.

Person vs. Organization Architecture

PartyKernel.Domain/EntitiesC#
// Party.Type is assigned at creation and is immutable through ordinary updates.
public class Party : AuditableEntity<Guid>, ITenantAwareEntity
{
    public Guid? TenantId { get; init; }
    public PartyType Type { get; init; }
    public string DisplayName { get; set; }
}

// Person names preserve the entered full name. Structured parts are optional;
// legacy FirstName/LastName fields are retained without manufacturing a split.
public class PartyPerson : AggregateRoot<Guid>, ITenantAwareEntity
{
    public Guid PartyId { get; init; }
    public string? FullName { get; set; }
    public string? GivenName { get; set; }
    public string? MiddleNames { get; set; }
    public string? FamilyName { get; set; }
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
}

public class PartyOrganization : AuditableEntity<Guid>, ITenantAwareEntity
{
    public Guid PartyId { get; init; }
    public string LegalName { get; set; }
    public string? TaxId { get; set; }
}

Party Model APIs

Tenant-scoped profile and lookup routesHTTP
# Profile uses the encrypted Party ID and a safe typed projection.
GET /api/v1/Parties/{encryptedPartyId}/profile
# Requires parties.view and party-people.view or party-organizations.view.

# Selector results expose encrypted Party IDs and safe summary fields only.
GET /api/v1/Parties/people/lookup?page=1&pageSize=20&search=...
GET /api/v1/Parties/organizations/lookup?page=1&pageSize=20&search=...
# Requires parties.view and the matching party-people.view / party-organizations.view.
# The server derives tenant scope from the authenticated caller; request tenant IDs are ignored.