Project Overview
Enterprise operations teams running complex equipment fleets frequently struggle with fragmented data: warranties tracked on spreadsheets, maintenance requests lost in email threads, and zero real-time visibility across global asset operations.
EquipOS was architected to replace these disconnected legacy tools with a single, highly auditable source of truth. As a core backend engineer, I designed the multi-tenant data layer, built the high-performance search engine, implemented the event-driven messaging pipelines, and developed a declarative workflow engine that eliminated costly custom code deployments for new enterprise tenants.
The Business Problem
Before modernizing the platform, our enterprise clients faced critical business bottlenecks:
- Revenue Leakage: Missed warranty claims and untracked service contracts caused thousands of dollars in unrecovered vendor costs each quarter.
- Data Isolation & Compliance Risks: Enterprise clients required strict physical/logical tenant isolation; any cross-tenant data visibility would trigger immediate compliance failure.
- Search Latency Collapse: As asset inventories surpassed hundreds of thousands of items, standard relational SQL queries with dynamic filters slowed down to 5–10 seconds, stalling daily call-center operations.
- Rigid Business Lifecycles: Every new enterprise customer had custom approval hierarchies for equipment claims, requiring bespoke code changes and separate deployments.
System Architecture
The backend was built as a clean, event-driven distributed system on Java 17 and Spring Boot, deployed on AWS with Docker and Kubernetes.
Key Architectural Invariants:
- Schema-per-Tenant Isolation: A custom request filter resolves tenant identity from the HTTP header/subdomain and dynamically binds the schema connection pool via a
RoutingDataSource. Data isolation is enforced at the database driver level—not via fragile application-levelWHEREclauses. - Transactional Outbox for Event Consistency: Domain state updates (e.g., claim approval, asset transfer) and their corresponding event messages are written in the same local database transaction. A dedicated relay publisher polls the outbox and streams messages to Apache Kafka with zero dual-write race conditions.
- Dedicated Read Models via Search DSL: We separated transactional OLTP writes from search queries by feeding domain events into Elasticsearch. Client search requests are compiled through a custom Abstract Syntax Tree (AST) parser that injects tenant constraints by construction.
My Specific Contribution
While collaborating with the broader platform team, my direct engineering responsibilities included:
- Multi-Tenant Routing Engine: Designed and implemented the complete request-scoped tenant resolution filter, ThreadLocal context propagation across worker thread pools (
TaskDecorator), and fail-closed security boundaries. - Enterprise Search Engine & AST Compiler: Built the search subsystem from scratch—parsing user query strings into validated AST nodes and compiling them into tenant-scoped Elasticsearch queries with Redis facet caching.
- Declarative Workflow State Machine: Engineered the deterministic state engine that allows administrators to configure custom multi-step claim approval stages via JSON configuration, eliminating per-tenant code branches.
- Database Query Tuning & Optimization: Profiled slow database queries with
EXPLAIN ANALYZE, restructured composite indexes, and optimized connection pool sizes to eliminate database CPU spikes.
Key Engineering Decisions & Trade-Offs
1. Schema-per-Tenant vs. Row-Level Multi-Tenancy
- Decision: Chose schema-per-tenant isolation for enterprise tiers.
- Rationale: Eliminates the catastrophic risk of forgotten SQL predicates leaking private data between competitors.
- Trade-Off: Higher database migration overhead across schemas, managed cleanly via automated Flyway migration pipelines.
2. Custom AST Search DSL vs. Dynamic ORM Specifications
- Decision: Hand-crafted a recursive-descent query parser compiling to Elasticsearch instead of using complex JPA Specification criteria.
- Rationale: JPA specifications generated unpredictable SQL joins and full table scans on large tables. The dedicated search index with pre-calculated facets guaranteed sub-150ms p95 latencies regardless of filter count.
3. Lightweight State Machine vs. Heavy BPMN Framework (Camunda)
- Decision: Built an internal deterministic state engine instead of adopting a full BPMN suite.
- Rationale: Reduced operational overhead, lowered container memory footprints by 60%, and made state transitions straightforward to debug and test with unit tests.