Skip to main content

Building the Nexus-Real Real Estate Trading Platform

Submitted by Anonymous (not verified) on
Designing a real estate trading platform focusing on deterministic pricing, consistent asset locking under high concurrency, and a flexible Modular Monolith architecture.

📌 Project Overview & My Role

  • Project: Nexus-Real-QNH — Core platform for transaction coordination, primary market pricing, and secondary market consignment for real estate enterprises.
  • My Role: Lead Fullstack Engineer / Solution Architect
  • Key Responsibilities:
    • Business requirement analysis, domain boundary analysis, and 3-tier DDD architecture design.
    • Designed and implemented the Pricing Pipeline Engine and Deterministic Rounding mechanism.
    • Designed high-concurrency cart processing workflow: Redis Distributed Lock combined with Database State Machine & Transactional Outbox.
    • Designed Data Schema (no cross-module DB Foreign Keys) and Dynamic Permission Registry.
    • Built Frontend SPA (React 19, TypeScript, Vite) and set up Production deployment environment (Linux, Nginx, PHP-FPM, Supervisor).

01. The Problem At real estate agencies with hundreds of brokers, primary and secondary sales face data conflicts with high legal and financial risks:

  1. Double Booking: When a premium block is launched, 3–5 brokers might simultaneously claim a deposit via chat groups or shared Excel spreadsheets.
  2. Cash Flow and Quote Discrepancies: Manual calculations on spreadsheets easily lead to errors due to rounding rules and inconsistent discount application priorities.
  3. Domain Fragmentation: Generic CRM systems managing flat customers tables cannot accurately represent the diverse lifecycle and roles of an individual (e.g., being a buyer, a homeowner, or an agency).

02. The Core Engineering Challenge The system is not just a simple CRUD application; it must guarantee 3 strict Business Invariants:

  • Invariant 1 — Deterministic Financial Output: For the same unit and price list version, cash flow calculations must be 100% reproducible, with the discrepancy always rounding down to 0 VND.
  • Invariant 2 — Single-Owner Asset State: A unit can only be in one state and belong to a single valid transaction session at any given time.
  • Invariant 3 — Legal Snapshot Immutability: Approved quotes and contracts must not change even if the original price list or future policies are updated.

 

03. Domain-Driven Design (DDD)

The system standardized the ubiquitous language and modeled it into independent Bounded Contexts. Party represents multi-role individuals/entities, and MarketAsset represents tradable assets. Quote and QuoteSnapshot represent commercial proposals and frozen legal records.

05. Pricing Engine: Deterministic Financial Output

Implemented an independent 10-step Pipeline pattern. Resolved calculation errors by:

  1. Applying progressive discounts by priority order on pre-tax prices.
  2. Using the Brick\Money\Money library and DECIMAL(18,4) in the DB, strictly rejecting float/double types.
  3. Eliminating rounding errors in the final installment via subtraction:  AmountN = GrandTotal - (Amount1 + Amount2 + ... + AmountN-1)

06. Preventing Double Booking Under High Concurrency

Combined Redis Lock as a Throttle/Contention Reducer (15m TTL) and Database Transaction + State Machine as the Single Source of Truth with a strict transition flow: AVAILABLE -> LOCKED -> RESERVED -> SOLD.

07. Quote Immutability

Data at the time of quoting is serialized into JSON, hashed via SHA-256, and saved into quote_snapshots (immutable). PDF quotes are rendered directly from this record to prevent tampering.

08. Asynchronous Synchronization (Transactional Outbox)

Events and business data are saved within the same DB Transaction to an Outbox Table, then a Worker Daemon dispatches them to a Redis Queue to ensure At-Least-Once Delivery.

04. System Architecture: Modular Monolith Deliberately rejected Microservices from the start. Adopted a 3-Tier Modular Monolith:

  • Platform/: Core foundation (Kernel DDD, Messaging, Storage).
  • Products/: Independent Bounded Contexts (CRM, Primary, Resale, Finance).
  • Integration/: Communication boundary layer.

"No Cross-Module Foreign Keys" Strategy: No physical Foreign Keys between modules (e.g., the quotes table only stores party_id). Consistency is guaranteed via Application Services and Domain Events instead of ON DELETE CASCADE. Bounded contexts are fully isolated and ready to be extracted into microservices if needed.

10. What We Deliberately Didn't Build

  • No Microservices: Team size did not warrant the operational complexity of distributed systems.
  • No Apache Kafka: The combination of MySQL Outbox Table + Redis Queue Workers is highly reliable and sufficient.
  • No Elasticsearch: Optimized MySQL indexing combined with Redis Cache efficiently resolved 95% of search queries.

11. Results & Metrics

  • Business Outcomes: Cash flow quotation time reduced from 45 minutes to < 1 minute. Transaction confirmation time reduced from 24 hours to < 6 hours. Double Booking conflicts were completely eliminated. Broker commissions are distributed instantly.
  • Engineering Metrics:
    • Supports 500 – 1,500 concurrent brokers.
    • Quote Calculation: Target < 500ms (p95).
    • Unit Lock API: Target < 1s.
    • Achieved strict Deterministic Output (Cash discrepancy = 0 VND).

12. Lessons Learned

  1. Domain Logic must be framework-agnostic: Keeping entities and financial calculations (Domain Layer) as pure PHP enabled lightning-fast Unit Testing and safe logic reuse across modules.
  2. Distributed Locks do not replace Database Integrity: Redis Lock manages traffic well, but final state changes must always be protected by Database Transactions and Atomic State Transitions.
  3. The value of "slowing down to model the domain": Spending time designing core concepts (Party, MarketAsset) upfront made it incredibly easy to expand to other modules (like Resale) without rewriting the core architecture.
Subscribe to React