feat: add unit and integration tests for rental management domain
- Introduced RentPaymentCorrectionTest and RentPaymentTest to validate payment and refund logic. - Added PhoneNumberTest to ensure proper normalization of phone numbers. - Created TenantTest to verify attachment management and tenant referencing. - Implemented ReplacementRentalApiJourneyIT for end-to-end testing of rental and payment workflows. - Documented architectural decisions in ADR-0014, emphasizing strict DDD boundaries and transaction management.
This commit is contained in:
1 parent
6ce0719a20
commit
e02ad54bcb
263 files changed
+10847
-7183
No files matched your search
@@ -4,6 +4,8 @@
|
||||
|
||||
Within Rental Management, `Lease` owns its contractual lifecycle, dates, rent terms, and lease-party relationships while referencing independent `Apartment` and `Tenant` aggregates by ID. Activation freezes the landlord identity and address, apartment address, tenant identities and addresses, guarantors, and financial terms; later changes are effective-dated immutable `LeaseAmendment` records that preserve both contract versions and may only recalculate future unpaid dues. `RentDue` and `RentPayment` are independent aggregates; a positive payment owns allocations whose sum cannot exceed the amount received, each allocation cannot exceed its due's remaining balance, and one payment may cover multiple dues only within the same lease. Any surplus stays unallocated. `Guarantor` remains internal to `Lease` until it gains an independent lifecycle or must be shared across leases.
|
||||
|
||||
A new `Lease` is always a mutable, permanently deletable `DRAFT`; draft overlap is allowed and creates no dues. Explicit activation rejects inclusive overlap with active or archived leases for the same apartment, then creates one uniquely constrained `RentDue` per occupied calendar month. Boundary months prorate rent and charges by occupied calendar days, while settlement and due-date timing remain independent facts on the due.
|
||||
A new `Lease` is always a mutable, permanently deletable `DRAFT`; draft overlap is allowed and creates no dues. Explicit activation reserves the period in the apartment's versioned `ApartmentOccupancy` aggregate, which retains active and archived lease periods and rejects inclusive overlap, then creates one `RentDue` per landlord, lease, and occupied calendar month under a unique database constraint. Making concurrent activations contend on the same occupancy document prevents write skew that a query-only overlap check would allow. Boundary months prorate rent and charges by occupied calendar days. An amendment split combines its before and after portions before performing one monetary rounding step, while settlement and due-date timing remain independent facts on the due.
|
||||
|
||||
Until Mongo transactions replace the single-instance lock, activation retries reconcile any dues left by a failed draft activation and draft dues remain hidden from queries.
|
||||
Lease activation, amendment reconciliation, payment confirmation and later allocation, reversal, and refund confirmation run in Mongo transactions whenever they change more than one aggregate document. Versioned aggregate documents reject stale concurrent writes; occupancy reservations, contract snapshots, generated or reconciled dues, payments, reversals, and refunds therefore commit or roll back without a single-process lock or manual database compensation. A payment owns at most one allocation per due, preventing two stale copies of one due from being mutated inside the same command.
|
||||
|
||||
Lease PDFs, payment receipts, and rent receipts are rendered from frozen contract versions. A payment receipt is available only for a confirmed payment and reports its complete amount, allocations, refunds, and remaining surplus. A rent receipt is available only for a fully settled due and reports exactly that due's rent and charges; it never treats unallocated payment surplus as rent income.
|
||||
@@ -1,5 +1,5 @@
|
||||
# Use pragmatic domain architecture in a modular monolith
|
||||
|
||||
**Status**: accepted
|
||||
**Status**: superseded by ADR-0014
|
||||
|
||||
Refactor the current backend as one Rental Management bounded context organized by domain capability, with business invariants enforced by aggregate behavior and thin HTTP/application orchestration. Keep Spring Data annotations on domain objects and avoid separate persistence models, ports, adapters, asynchronous events, or deployable services until a real boundary requires them; the future LMNP Tax and Accounting Core Domain will be a separate context consuming rental facts.
|
||||
@@ -0,0 +1,20 @@
|
||||
# Use strict DDD boundaries for Rental Management
|
||||
|
||||
**Status**: accepted
|
||||
|
||||
ADR-0012 kept the first Rental Management refactor deliberately pragmatic. That structure left business rules coupled to Spring Data documents and technical-layer packages, and its single-process integrity lock could not protect multiple application instances.
|
||||
|
||||
The backend now uses one modular-monolith bounded context with:
|
||||
|
||||
- framework-independent aggregates, value objects, domain events, rules, and repository contracts under `rental/domain`;
|
||||
- application use cases and outbound ports under `rental/application`;
|
||||
- HTTP/OIDC inbound adapters and MongoDB, filesystem, identity, and PDF outbound adapters under `rental/adapter`;
|
||||
- explicit composition in `RentalApplicationConfiguration`.
|
||||
|
||||
Spring Data documents are private to the MongoDB adapters. They map domain versions to `@Version` fields and every portfolio query and mutation includes the authenticated `LandlordId`; landlord identity registration uses the deterministic `oidc:<subject>` landlord ID. The application disables legacy Spring Data repository discovery so obsolete persistence models and indexes cannot become active accidentally. Because repository auto-configuration is disabled, the Mongo adapter explicitly resolves and creates the replacement documents' annotated indexes at startup, including unique integrity constraints.
|
||||
|
||||
Multi-aggregate operations use MongoDB transactions and optimistic aggregate versions. Development and integration-test MongoDB therefore run as a replica set. A versioned apartment-occupancy aggregate serializes competing lease activations for the same apartment; versioned dues and payments reject concurrent balance changes; activation, amendments, allocation, reversal, and refund confirmation commit or roll back as one unit. The single-process `RentalIntegrityLock` and manual database compensation are not part of the new runtime.
|
||||
|
||||
The filesystem cannot join a MongoDB transaction. Attachment deletion therefore stages a path-addressed, recoverable file move before the version-checked Mongo mutation, restores it when that mutation fails, and performs tombstone cleanup after success. On startup, uncommitted staging is restored and explicitly committed tombstones are discarded, preventing Mongo metadata from pointing at a file lost during a process crash. This local-filesystem recovery assumes one backend process owns the attachment root; horizontal scaling requires shared transactional object storage or an external reconciliation workflow. The boundary is explicit in the application storage port rather than hidden behind a database transaction annotation.
|
||||
|
||||
This supersedes ADR-0012 without introducing microservices, event sourcing, a message broker, or a second state-management mechanism. Existing REST contracts and development Mongo documents are intentionally replaceable; issue 24 explicitly permits resetting development data and requires no production migration.
|
||||
Reference in new issue
Block a user