intermittent commit
This commit is contained in:
193
docs/ADDRESS_MODULE_ARCHITECTURE.md
Normal file
193
docs/ADDRESS_MODULE_ARCHITECTURE.md
Normal file
@@ -0,0 +1,193 @@
|
||||
# GovOPlaN Addresses Module Architecture
|
||||
|
||||
## Decision
|
||||
|
||||
`govoplan-addresses` owns reusable contact and recipient-source data. Campaigns,
|
||||
mail, scheduling, portal, postbox, forms, reporting, and other modules consume
|
||||
address data through core-mediated capabilities and APIs, not by importing
|
||||
address-module internals.
|
||||
|
||||
The implementation reference for contact data is vCard. CardDAV is the primary
|
||||
address-book sync protocol. LDAP/Active Directory, Exchange/Microsoft 365,
|
||||
Google Contacts, CSV/XLSX, LDIF, and batch vCard import/export are connector
|
||||
targets layered on top of the same local model and sync contracts.
|
||||
|
||||
## Ownership
|
||||
|
||||
`govoplan-addresses` owns:
|
||||
|
||||
- scoped address books
|
||||
- contacts, organizations, households, and postal/email/phone address data
|
||||
- vCard import/export and vCard-compatible field mapping
|
||||
- reusable recipient sources and classical address lists
|
||||
- contact tags, categories, communication preferences, consent, and legal basis
|
||||
- deduplication, merge, address quality checks, and suppression lists
|
||||
- contact provenance, audit history, soft delete, and restore
|
||||
- external-source bindings, sync state, conflicts, and read-only source markers
|
||||
|
||||
It does not own:
|
||||
|
||||
- campaign-local recipient snapshots and evidence
|
||||
- mail transport, mailbox access, or delivery queues
|
||||
- calendar events or iCalendar event storage
|
||||
- global identity authentication or authorization decisions
|
||||
- organization structure or internal function assignments
|
||||
- operational distribution lists/`Verteiler` with mixed users, identities,
|
||||
groups, functions, roles, raw recipients, and nested lists
|
||||
|
||||
## Scopes
|
||||
|
||||
Address books can live in these scopes:
|
||||
|
||||
- `user`: personal address books and remembered contacts
|
||||
- `group`: team/shared address books
|
||||
- `tenant`: tenant-wide directories and approved shared lists
|
||||
- `system`: platform-wide public/shared directories where policy allows it
|
||||
|
||||
The scope determines visibility, default permissions, sync credentials, and
|
||||
whether downstream modules may reuse or mutate entries.
|
||||
|
||||
## Data Model Principles
|
||||
|
||||
The canonical model should preserve enough vCard semantics to round-trip common
|
||||
fields:
|
||||
|
||||
- name components and formatted names
|
||||
- nicknames and display names
|
||||
- email addresses, phone numbers, postal addresses, URLs, notes, categories
|
||||
- organizations, titles, roles, departments, and relationships
|
||||
- birthday/anniversary where allowed by policy
|
||||
- photos/avatars where storage and privacy policy allow them
|
||||
- calendar or scheduling addresses where present
|
||||
- source IDs, revisions, ETags, sync tokens, and provenance
|
||||
|
||||
The model should support both normalized query fields and a preserved original
|
||||
representation for import/export and conflict handling.
|
||||
|
||||
The local baseline implements scoped address books, contacts, normalized
|
||||
email/phone/postal-address tables, tags, source kind/reference fields,
|
||||
first-class source payload/revision fields, and provenance JSON. Imported
|
||||
vCards preserve raw source payload and revision metadata for audit/debugging.
|
||||
Sync sources, attempt state, tombstones, conflicts, and diagnostics are now
|
||||
first-class backend tables and API resources. Connector-specific diffing,
|
||||
CardDAV discovery, and conflict-resolution UI remain part of the connector
|
||||
milestones.
|
||||
|
||||
## Capabilities
|
||||
|
||||
The first stable capabilities are:
|
||||
|
||||
- `addresses.recipient_source`: return immutable recipient snapshots for
|
||||
campaigns, forms, reporting, and other send/build workflows.
|
||||
- `addresses.lookup`: provide read-only lookup and autocomplete for mail,
|
||||
campaign, scheduling, postbox, portal, and case workflows.
|
||||
- `addresses.contact_writer`: provide address-book-scoped write target decisions
|
||||
and contact creation for local or otherwise writable sources.
|
||||
|
||||
Capabilities use DTOs and source IDs. Consumers must not receive ORM objects or
|
||||
write address tables directly. Consumers that need historical evidence must
|
||||
store their own immutable snapshot with source ID, source revision, and
|
||||
provenance; they must not treat live address records as historical evidence.
|
||||
|
||||
`addresses.recipient_source` exposes both complete address books and classical
|
||||
address lists. Address-book sources use `addresses:address_book:<id>`.
|
||||
Address-list sources use `addresses:address_list:<id>` and include the
|
||||
address-list entry ID in each recipient's provenance. The current snapshot DTO
|
||||
is email-recipient oriented; postal-only list entries are valid address-list
|
||||
members but are skipped by the email recipient-source path until postal
|
||||
recipient DTOs are added.
|
||||
|
||||
The writer capability is intentionally address-book specific. It answers
|
||||
whether the current principal may perform an operation such as `create_contact`,
|
||||
`update_contact`, or `delete_contact` against a concrete address book. The
|
||||
decision payload includes:
|
||||
|
||||
- `allowed`
|
||||
- stable `reason`
|
||||
- user-facing `message`
|
||||
- required scopes
|
||||
- source kind
|
||||
- read-only state
|
||||
- scope and tenant provenance
|
||||
|
||||
Policy modules or connector sync state may later add inputs to this decision,
|
||||
but consumers must continue to call the address capability/API instead of
|
||||
importing policy logic or address services directly. Disabled or read-only UI
|
||||
actions should surface the returned `message` on hover.
|
||||
|
||||
## Sync Model
|
||||
|
||||
Every synced address book tracks or can track:
|
||||
|
||||
- connector type and external account/source
|
||||
- external address-book ID and display name
|
||||
- local address-book scope
|
||||
- sync direction: read-only, one-way import, one-way export, two-way
|
||||
- sync token, ETag/revision, last successful sync, last attempted sync
|
||||
- deleted markers/tombstones
|
||||
- conflict status and resolution decision
|
||||
- connector diagnostics and rate-limit/backoff state
|
||||
|
||||
Sync conflict UX must show the local value, remote value, source, timestamp, and
|
||||
available action. Silent overwrite is not acceptable.
|
||||
|
||||
Sync infrastructure is intentionally connector-neutral. CardDAV, LDAP,
|
||||
Exchange/Microsoft 365, Google Contacts, CSV/XLSX/LDIF import profiles, and
|
||||
future connectors must write through `addresses_sync_sources` and related
|
||||
records instead of inventing connector-specific status tables. Connector jobs
|
||||
may mark a source `running`, `succeeded`, `failed`, or `conflict`; read-only and
|
||||
one-way-import sources propagate a read-only decision to the owning address
|
||||
book, which in turn blocks normal contact writes through the existing writer
|
||||
capability/API.
|
||||
|
||||
The first CardDAV implementation supports discovery, source binding, dry-run
|
||||
preview, inbound vCard sync, outbound create/update/delete for writable
|
||||
sources, sync-token/full-sync fallback, tombstones, diagnostics, and persisted
|
||||
conflicts. Outbound writes use ETag preconditions; stale local state must become
|
||||
a conflict instead of silently overwriting remote data. The first conflict
|
||||
review UI compares stored local and remote field payloads and can apply a
|
||||
stored remote vCard payload or a manual per-field local/remote merge payload.
|
||||
Source disconnect/delete removes the source binding and related sync records
|
||||
while keeping local contacts.
|
||||
|
||||
## Connector Direction
|
||||
|
||||
Implement connectors in this order:
|
||||
|
||||
1. vCard import/export and batch import.
|
||||
2. CardDAV address books.
|
||||
3. LDAP/Active Directory read-only directories.
|
||||
4. Exchange/Microsoft 365 and Google Contacts.
|
||||
5. CSV/XLSX/LDIF import mapping profiles.
|
||||
|
||||
Connector runtime behavior should reuse shared connector concepts where useful:
|
||||
configured endpoints, credentials, dry-run, diagnostics, rate limits, and audit
|
||||
events.
|
||||
|
||||
## Cross-Module Integration
|
||||
|
||||
Campaigns should consume `addresses.recipient_source` through the platform
|
||||
registry and freeze snapshots into campaign versions. Mail should consume
|
||||
`addresses.lookup` for autocomplete and `addresses.contact_writer` for "add
|
||||
contact" workflows. Scheduling should use lookup for attendees and organizers.
|
||||
Portal, postbox, cases, forms, and reporting should link to contact records by
|
||||
stable IDs while keeping their own domain evidence. Cross-module UI must hide
|
||||
write actions when no writable target exists, or show the writer decision
|
||||
message when a disabled action remains visible for context.
|
||||
|
||||
Operational distribution lists belong in `govoplan-dist-lists`. They may later
|
||||
consume address lists as one entry type, but they own mixed recipient expansion
|
||||
for users, identities, organization units, groups, functions, roles, raw
|
||||
recipients, and nested lists. Workflow and Tasks own `Umlauf` execution state;
|
||||
distribution lists define who is included, not how work circulates.
|
||||
|
||||
## Deferred Work
|
||||
|
||||
The following are valuable but not required for the first functional milestone:
|
||||
|
||||
- automatic deduplication and merge suggestions
|
||||
- two-way sync conflict UI
|
||||
- Microsoft/Google connectors
|
||||
- household and relationship editing
|
||||
- advanced consent-policy automation
|
||||
- contact activity timeline across all modules
|
||||
Reference in New Issue
Block a user