Tactical Design & Clean Architecture
A rigorous implementation of Clean Architecture principles in Golang, decoupling core financial domain logic from volatile infrastructure and delivery frameworks.
Engineered a Clean Architecture across 6 Golang microservices. Achieved a highly testable domain layer by strictly enforcing Dependency Inversion, ensuring business rules remain completely isolated from gRPC protocols, NATS events, and PostgreSQL/GORM schemas.

The Challenge & Impact
- The Challenge: Financial systems require handling real-time data streams, complex business rules, and high-throughput API requests simultaneously. Tying these directly to database schemas or web frameworks creates a brittle "Big Ball of Mud," leading to race conditions, memory leaks, and untestable code.
- The Objective: To enforce a strict architectural boundary—where dependencies only point inward toward the domain—while heavily optimizing the Delivery layer for concurrency and the Repository layer for query performance.
- The Impact: Delivered a highly concurrent, memory-safe backend. Standardized the codebase by strictly separating DTOs, Domain Objects, and DB Schemas, which eliminated data leakage and enabled granular, highly parallel unit testing across all layers.
Architecture & Execution
Tech Stack
Go, Clean Architecture, FastHTTP, gRPC, WebSockets, GORM, PostgreSQL, Redis
1. The Layered Dependency Model
To implement this architecture, I meticulously designed dedicated data models for each layer to ensure a clear separation of concerns:
- Domain (Entities): Pure Go structs defining core financial business rules and invariants.
- Usecase (Execution & Inter-Service): The core orchestrator of the system. Its responsibilities are strictly scoped to:
- Receiving DTOs from the Controller and mapping them to internal Domain Objects.
- Coordinating the execution flow of complex business logic.
- Managing domain state via abstracted Repository interfaces.
- Handling cross-boundary, inter-service gRPC communications.
- Repository (DB Schema Objects): Infrastructure implementation. Executes queries and maps underlying DB schemas directly to Domain Objects.
- Controller (gRPC Entry Point): The primary gRPC entry point, callable by the API Gateway or other internal microservices. Its responsibilities are strictly scoped to:
- Creating and validating Data Transfer Objects (DTOs).
- Orchestrating the appropriate Usecases.
- Dispatching system and domain events.
- Translating internal Domain errors into standardized gRPC status codes for the client.
👉 Deep Dive on the LZStock Tech Blog: Back-end with DDD and Clean Architecture
2. Tactical Domain Modeling
To ensure the core business logic remains pristine and framework-agnostic, I strictly adhered to Domain-Driven Design (DDD) tactical patterns within the Domain Layer. By clearly defining the responsibility boundaries for Aggregates, Entities, and Value Objects, I prevented business logic from leaking into the UseCase or Infrastructure layers.
Aggregate (The Transactional Boundary)
Enforces overarching business rules, domain constraints, and cross-entity validation. It acts as the single entry point (Root) to guarantee data consistency.
Examples: Business limit enforcement (e.g., maximum daily trade volume), duplicate transaction checks, and domain-level permission logic.
Entity (Identity & Lifecycle)
Maintains unique identity over time. Handles entity completeness, required fields, and basic structural validation within its own scope.
Examples: Unique ID validation, checking required fields upon creation, and maintaining basic structural integrity.
Value Object (Immutable Traits)
Represents a descriptive aspect of the domain with no conceptual identity. Handles format, length, character rules, and primitive value validation.
Examples: Email format validation, price range constraints, and currency string length checks.
👉 Deep Dive on the LZStock Tech Blog: Domain Object Design
3. Algorithmic Highlight: Autocomplete Search (Ternary Search Tree)
Instead of relying on slow database LIKE queries for stock ticker auto-completion, I implemented a Ternary Search Tree (TST) in memory.
- Execution: The tree building process is initialized asynchronously. I utilized an error channel (
err chan) to gracefully collect and aggregate errors from concurrent goroutines during the build phase. - Result: This highly optimized TRIE data structure drastically reduced search latency, providing buttery-smooth UX for users searching through thousands of financial symbols.
👉 Deep Dive on the LZStock Tech Blog: Full implementation of Auto Complete Search Tree
Advanced Mechanics 1: Concurrency & Background Tasks
Thread-safe Session Management for Stock Price Streaming
⚙️ Check details
- Dual-Level RWMutex Strategy: Implemented a thread-safe session manager to maximize throughput for concurrent price updates, preventing race conditions during high-frequency API calls.
- Self-Healing Lifecycle Manager: Developed a background routine with TTL (Time-To-Live) logic to automate resource reclamation and prevent memory leaks.
👉 Deep Dive on the LZStock Tech Blog: Thread-safe Session Management
Goroutine Orchestration
⚙️ Check details
Utilized the errgroup package to concurrently execute and coordinate multiple goroutines, ensuring graceful shutdown and centralized error propagation across gRPC streaming and FastHTTP WebSocket tasks.
👉 Deep Dive on the LZStock Tech Blog: Websocket Integration
Go Generics Worker Pool
⚙️ Check details
Implemented a robust, concurrent Worker Pool leveraging Go's Genericity (Go 1.18+). This foundation allows for specialized, reusable type workers, ensuring high performance, maximum flexibility, and compile-time type safety for background tasks.
👉 Deep Dive on the LZStock Tech Blog: Background Tasks
Trigger Methods Invocation Rule
⚙️ Check details
Established clear rules of thumb for internal service invocation:
- Controller: Single entry point for External/Traced Internal Events.
- Usecase: Entry point for Untraced, Complex Logic (Event/Worker).
- Repository: Entry point for Untraced, Simple CRUD (Event/Worker).
👉 Deep Dive on the LZStock Tech Blog: Trigger Methods
Advanced Mechanics 2: Error Handling & Testing Strategy
The Error Chain & Recovery
⚙️ Check details
- Three-Layered Scope: Implemented granular error handling (App internal error, gRPC error code, HTTP error). Internal details are strictly masked from end-users to prevent information leakage.
- Error Wrapping: Utilized
fmt.Errorfwith the%wverb for chaining, ensuring traceable logs. - Standardization: Centralized gRPC error handling via a
HandleGRPCErrutility and mapped internal errors to user-friendly UI messages using aCreateDashboardErrMessageMap. - Panic Recovery: Developed a custom
Recoverutility to gracefully catch fatal panics and dump formatted stack traces for debugging without crashing the service.
👉 Deep Dive on the LZStock Tech Blog: Error Handling
Strategic Testing Focus
⚙️ Check details
By utilizing Dependency Inversion, testing is strategically distributed:
- Domain: Unit Testing to verify core invariants in isolation.
- Use-Case: Mocking external dependencies to ensure correct logic orchestration and error handling.
- Repository: Integration Testing for data correctness and concurrency handling.
- Controller: Verifying API contracts, request mapping, and the error propagation chain.
- E2E Testing: Validating the complete request-response cycle (including Auth) from the client's perspective.
👉 Deep Dive on the LZStock Tech Blog: Testing Strategies
Advanced Mechanics 3: Database Design & Optimization
Pagination Strategy Evaluation
⚙️ Check details
Analyzed and implemented appropriate strategies based on use cases. Chose Cursor-based pagination for infinite scrolling performance, while evaluating Offset-based (for direct page access) and Database Cursor mechanisms.
👉 Deep Dive on the LZStock Tech Blog: Design Pagination Strategies
Preventing N+1 Queries
⚙️ Check details
Solved the N+1 problem by evaluating the trade-offs between GORM Eager Loading (Preloading) versus direct JOIN queries.
👉 Deep Dive on the LZStock Tech Blog: Prevent N+1 Problem
Execution Plans & Indexing
⚙️ Check details
Leveraged SQL Execution Plans for query tuning. Researched and mapped a scalability roadmap including B-tree, Hash, GIN (for Full-Text Search/JSONB), Covering Indexes (for index-only scans), and Partial Indexes to reduce index bloat.
👉 Deep Dive on the LZStock Tech Blog: SQL Execution Plans for Query Tuning
👉 Deep Dive on the LZStock Tech Blog: Indexing Exploration
Advanced Mechanics 4: API Gateway, Security
FastHTTP Integration
⚙️ Check details
- Utilized high-performance libraries (FastHTTP and FastHTTP-routing) for efficient request handling.
- Implemented robust versioning for the RESTful API endpoints.
- Integrated gRPC backend services by wrapping connections within the RESTful API layer.
👉 Deep Dive on the LZStock Tech Blog: API Design and Versioning
Security Middleware
⚙️ Check details
- Security Middleware: Implemented Route-based Access Control (RBAC) for authentication and a robust CORS strategy separating Development (loose) and Production (strict) environments.
👉 Deep Dive: See more details on the LZStock tech blog: RBAC 👉 Deep Dive: See more details on the LZStock tech blog: CORS Middleware
🌟 Present-Day Retrospective
Algorithmic & Architectural Reflections on State & Scaling:
- Algorithmic Debt: TST Balancing: While the in-memory Ternary Search Tree (TST) vastly outperformed database queries for autocomplete, the initial asynchronous build phase revealed a critical structural flaw. Because the stock tickers were ingested and inserted alphabetically (A to Z), the tree became highly unbalanced, essentially degrading its time complexity from $O(\log N)$ to $O(N)$ in worst-case branches. To resolve this, future iterations will pre-sort the dataset and insert elements recursively from the median, guaranteeing a perfectly balanced tree structure and predictable sub-millisecond latency.
- The Distributed State Bottleneck: While the dual-level
RWMutexand self-healing lifecycle manager worked flawlessly for a single-node setup, I quickly identified horizontal scaling bottlenecks in local state management. This reflection is driving the proposed migration toward distributed event-streaming via NATS JetStream to completely decouple the streaming engine state.- Worker Pool Evolution: The Go Generics Worker Pool provides excellent compile-time type safety and performance for local background tasks. However, for a fully resilient microservices architecture, I plan to transition these asynchronous jobs to a JetStream-based event-driven approach, ensuring persistence and guaranteed delivery across service restarts.