Registry Service
Service Type: Core REST Microservice with Asynchronous Job Processing Language: Kotlin / Micronaut Framework Database: PostgreSQL (owned) Async Processing: JobRunr (scheduled and background tasks) Status: Implemented (v1.32.0)
Overview
Registry Service (internally branded as "Governance Service") manages the complete lifecycle of bond issuance, company governance, and market participants within DEUSS. It orchestrates companies (issuers/investors), bonds, guarantees, scoring, and associated documents while coordinating asynchronous blockchain operations and document storage. This is a complex, stateful service with integration to blockchain and external file storage.
Business Responsibilities
- Company Management — Lifecycle of issuer and investor companies, roles, profiles, wallet addresses, AML verification states
- Bond Lifecycle — Creation, drafting, publishing, issuance of bonds; tracking ISIN codes, nominal values, interest rates, maturity dates
- Bond Guarantees — Management of guarantee structures, guarantee documents, and guarantee states for bonds
- Document Management — Upload, storage, and retrieval of bond documents and guarantee documents (via S3)
- Marketing Assets — Storage and management of bond marketing materials
- User & Passkey Management — User registration with passkey support (WebAuthn); identity recovery
- Scoring — Record and track company scoring events (credit ratings, risk assessments, etc.)
- Blockchain Operations — Coordinate asynchronous blockchain writes via JobRunr; track operation status
- Async Job Execution — Bond issuance, company creation, manager changes, freeze operations via JobRunr
Owned Data
Core Entities:
users— User accounts with email, passkeys, wallet addressescompanies— Master company records (name, identifiers, country, wallet address)issuer_details— Profile for companies issuing bondsinvestor_details— Profile for companies investing in bondsbonds— Bond records (ISIN, nominal value, interest rate, maturity, state: DRAFT/WAITING_FOR_ISSUANCE/ISSUED)bond_industry_codes— Industry classification codes for bondsbond_documents— Uploaded bond prospectuses and related documentsbond_marketing_assets— Marketing materials for bondsbond_guarantees— Guarantee arrangements for bondsbond_guarantee_documents— Documents related to bond guaranteesscorings— Historical scoring records for companiesoperations— Records of asynchronous blockchain operations with status trackingpasskeys— WebAuthn passkeys for user identity Constraints:- One issuer profile per company (unique)
- One investor profile per company (unique)
- ISIN codes are unique
- Bonds tracked in states: DRAFT → WAITING_FOR_ISSUANCE → ISSUED
- Users have optional email (nullable post-migration)
Inbound Interfaces
REST API
Authentication: JWT Bearer token; varies by endpoint (ANONYMOUS for public, ADMIN for admin)
Companies Management (/v1/companies, /v1/admin/companies)
| Endpoint | Method | Operation | Purpose |
|---|---|---|---|
/v1/companies | GET | getCompanies | List companies with filters (name, acting user, distributor, business ID, VAT, LEI, AML state, wallet, country) |
/v1/companies/{company_id} | GET | getCompany | Fetch company details |
/v1/companies/issuer | POST | createIssuerCompany | Create company with issuer profile (async, returns 202) |
/v1/companies/investor | POST | createInvestorCompany | Create company with investor profile (async, returns 202) |
/v1/companies/{company_id}/issuer-profile | POST | addIssuerProfile | Add issuer profile to existing company |
/v1/companies/{company_id}/investor-profile | POST | addInvestorProfile | Add investor profile to existing company |
/v1/companies/{company_id} | PATCH | updateCompany | Update company attributes |
/v1/companies/{company_id}/manager | PATCH | changeCompanyManager | Change acting manager (async operation) |
Bonds Management (/v1/bonds, /v1/admin/bonds)
| Endpoint | Method | Operation | Purpose |
|---|---|---|---|
/v1/bonds | GET | getBonds | List bonds with filters (name, company, registration, headcount, guarantor) |
/v1/bonds/{bond_id} | GET | getBond | Fetch bond draft details |
/v1/bonds/isin/{bond_isin} | GET | getBondByIsin | Fetch bond by ISIN code |
/v1/bonds | POST | createBondDraft | Create new bond draft |
/v1/bonds/{bond_id} | PATCH | patchBondDraft | Update bond draft fields |
/v1/bonds/{bond_id} | DELETE | deleteBondDraft | Delete draft bond |
/v1/bonds/{bond_id}/issue | POST | issueBond | Issue bond (async, triggers blockchain ops) |
Bond Documents (/v1/bonds/{bond_id}/documents)
| Endpoint | Method | Operation | Purpose |
|---|---|---|---|
/v1/bonds/{bond_id}/documents/upload | POST | initiateBondDocumentUpload | Begin file upload (returns upload_id) |
/v1/bonds/{bond_id}/documents/upload/{upload_id}/complete | POST | completeBondDocumentUpload | Finalize upload to S3 |
/v1/bonds/{bond_id}/documents | GET | getBondDocuments | List uploaded documents |
/v1/bonds/{bond_id}/documents/{document_id} | DELETE | deleteBondDocument | Delete document |
Bond Marketing Assets (/v1/bonds/{bond_id}/marketing-assets)
| Endpoint | Method | Operation | Purpose |
|---|---|---|---|
/v1/bonds/{bond_id}/marketing-assets/upload | POST | initiateBondMarketingAssetUpload | Begin marketing asset upload |
/v1/bonds/{bond_id}/marketing-assets/upload/{upload_id}/complete | POST | completeBondMarketingAssetUpload | Finalize marketing asset to S3 |
/v1/bonds/{bond_id}/marketing-assets | GET | getBondMarketingAssets | List marketing assets |
/v1/bonds/{bond_id}/marketing-assets/{asset_id} | DELETE | deleteBondMarketingAsset | Delete marketing asset |
Bond Guarantees (/v1/bonds/{bond_id}/guarantees)
| Endpoint | Method | Operation | Purpose |
|---|---|---|---|
/v1/bonds/{bond_id}/guarantees | GET | getBondGuarantees | List guarantees for bond |
/v1/bonds/{bond_id}/guarantees | POST | createBondGuarantee | Create guarantee for bond |
/v1/bonds/{bond_id}/guarantees/{guarantee_id} | PATCH | updateBondGuarantee | Update guarantee status/details |
Users (/v1/users)
| Endpoint | Method | Operation | Purpose |
|---|---|---|---|
/v1/users | POST | createUser | Create new user |
/v1/users/{user_id}/wallet | POST | setUserWallet | Set blockchain wallet address for user |
/v1/users/{user_id}/passkey | POST | registerPasskey | Register WebAuthn passkey |
/v1/users/{user_id}/recover | POST | recoverUser | Recover user identity via passkey |
Scoring (/v1/scoring)
| Endpoint | Method | Operation | Purpose |
|---|---|---|---|
/v1/scoring | POST | recordScoring | Record company scoring event (credit rating, risk, etc.) |
Operations (/v1/operations)
| Endpoint | Method | Operation | Purpose |
|---|---|---|---|
/v1/operations/{operation_id} | GET | getOperation | Query status of async operation (blockchain job) |
Outbound Interfaces
Internal Service Calls
- Company Service (Broker Platform) — Generated OpenAPI client for company operations (internal integration)
External Integrations
- AWS S3 — Document and marketing asset storage
- Bucket: Configurable via
AWS_S3_BUCKET - Operations: Upload/download bond docs, marketing assets, guarantee docs
- Bucket: Configurable via
- Blockchain — Asynchronous blockchain writes via JobRunr
- Company entity type ID: Configurable (default: 1)
- Wallet type/role: Configurable for company wallets
- Operations: Create issuer/investor on-chain, manage wallets
Database Only
- PostgreSQL direct access via Hibernate/JPA
Kafka Integration
Status: Disabled (kafka.enabled: false)
- No Kafka producers
- No Kafka consumers
- No CDC/Debezium enabled
- Async model uses JobRunr instead
External Integrations
- AWS S3 — Bond documents, marketing assets, guarantee documents stored with presigned URLs
- Blockchain (On-Chain Registry) — Companies created/updated on blockchain via JobRunr jobs
- Entity type IDs for issuers/investors
- Wallet management (EOA or smart contract wallets)
- Company Service (Broker Platform) — OpenAPI client for company-related operations
- JobRunr — Distributed job scheduler for async operations
Asynchronous Job Model
Registry Service uses JobRunr for background task execution:
| Job | Trigger | Purpose | Retry Logic |
|---|---|---|---|
IssueBondJob | Bond issuance request | Register bond on blockchain, finalize state | Automatic with custom filters |
CreateCompanyJobs | Company creation (issuer/investor) | Create company wallet on-chain | Retry on blockchain errors |
ChangeManagerJobs | Manager change request | Update manager on-chain | Retry on blockchain errors |
FreezeJobs | Freeze operation | Lock/freeze company account on-chain | Custom handling |
PublishBondJob | Bond publication | Publish bond details on-chain | Retry logic |
AddScoringJob | Scoring record | Record score on-chain | Retry logic |
IssueBondScheduler | Periodic (default 5min interval) | Poll and process pending issuance | Configurable interval |
Configuration:
registry:
issue-bond-scheduler:
interval: ${ISSUE_BOND_SCHEDULER_INTERVAL:PT5M}
Key Business Rules
- Companies must have at least one role (ISSUER or INVESTOR)
- Bonds progress through states: DRAFT → WAITING_FOR_ISSUANCE → ISSUED
- Bond Drafts can only be deleted; published bonds cannot be modified
- ISIN Codes are unique and immutable once assigned
- Bond Guarantees are tied to specific bonds and track guarantee state/documents
- User Passkeys enable passwordless recovery; email is optional
- Scoring events are immutable audit records
- Operations track all async blockchain jobs with success/failure state
- AML Verification States filter companies by compliance status (UNKNOWN, VERIFIED, REJECTED, EXPIRED)
- File Uploads use 2-stage process: initiate (get upload_id) → complete (finalize to S3)
Deployment
Runtime Stack:
- Java 25 (OpenJDK JRE in Alpine container)
- Micronaut on Netty
- PostgreSQL database with Flyway migrations (28 migration versions)
- JobRunr for distributed job scheduling
- Kotlin coroutines for async/await patterns Configuration:
- Port: 8080
- Health check:
/health - Environment variables:
DATABASE_URL,DATABASE_USER,DATABASE_PASSWORDAWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_REGION,AWS_S3_BUCKET,AWS_S3_HOSTCHAIN_COMPANY_ENTITY_TYPE_ID,CHAIN_COMPANY_WALLET_TYPE,CHAIN_COMPANY_WALLET_ROLEISSUE_BOND_SCHEDULER_INTERVALJWT_GENERATOR_SIGNATURE_SECRETContainer:
- Non-root user (
runtime) for security - Init process (
tini) for signal handling - JVM container support enabled
Observability
- Structured Logging — Semantic logging with events and parameters
- Health Endpoint —
/healthfor K8s liveness/readiness - Job Status Tracking — Operations table tracks job success/failure/retry count
- Metrics — Unknown (Micrometer integration status not verified)
Technical Notes
- Async-First Model — Company creation and bond issuance return 202 Accepted; clients must poll
/v1/operations/{operation_id}for completion - Job Scheduler Pattern —
IssueBondSchedulerpolls pending issuances every 5 minutes; configurable interval - Two-Stage File Upload — Security pattern: request upload slot, get presigned URL, complete upload to confirm
- Custom Job Retry Filters —
JobRetryExhaustedFilter,TxNotMinedYetExceptionfor blockchain-specific handling - Coroutine Support — Uses Kotlin coroutines for async I/O patterns
- CBOR Serialization — Jackson CBOR support for compact binary serialization
- Company Visibility — Companies can be filtered by visibility states (public/private)
- Profile Status & Frozen State — Companies/users can be frozen with
frozenUntiltimestamp
Relationships with Other Services
- Partner Service (core) — Unknown; likely separate governance domains (Partner = payment/fees, Registry = governance/bonds)
- Core Payment Service (core) — Unknown; Registry may coordinate payments or receive payment notifications
- Company Service (Broker Platform) — Active integration via OpenAPI client for company operations
- Blockchain — Direct blockchain writes for company/bond entities
- Indexer (GraphQL) — May expose registry data via GraphQL (Unknown—not verified)
Documentation Sources:
- Source:
core/registry-service/src/ - Migrations:
core/registry-service/src/main/resources/db/migration/(28 versions) - OpenAPI Public:
api-specifications/.../registry-service/public/ - OpenAPI Internal:
api-specifications/.../registry-service/internal/ - Config:
core/registry-service/src/main/resources/application.yaml - Container:
core/registry-service/Dockerfile