Skip to content

Architecture Overview

Blindstrader is built as a microservices monorepo — each service has its own codebase, Docker container, database, and deployment lifecycle, but they all live in the same Git repository for coordinated development.

Microservices Map

ServiceResponsibilityDatabasePort
IdentitySSO auth, RBAC, partner discovery, connectionsGlobal MySQL8001
BrandTenant federation, catalog management, permission enforcementPer-Tenant MySQL8002
SupplierSupplier operations, pricing, inventory, fulfillmentPer-Tenant MySQL8003
Supply ChainOrder orchestration, Saga coordination, fulfillment rulesOperational MySQL8004
PaymentStripe Connect, payment splits, ledgers, refundsFinancial MySQL8005
RetailerCustomer storefronts, catalog overrides, local pricingPer-Tenant MySQL8006
Platform OpsBilling, analytics, system administration (Filament)Management MySQL8007
NotificationTransactional email, SMS, Slack alertsUtility MySQL8008

Core Architectural Principles

1. Database-per-Service

Each microservice has its own dedicated operational database. No two services share a database schema. Cross-service data access is via the event bus or authenticated HTTP API calls — never direct DB joins.

2. Database-per-Tenant

Every Brand, Supplier, and Retailer organisation also gets its own isolated MySQL database. One tenant's query load cannot affect another tenant's performance.

3. Event-Driven Communication

Services communicate asynchronously via Apache Kafka. Services emit events when something significant happens; other services subscribe and react. No synchronous service-to-service calls for core business logic.

4. Stateless Federation (Brand Service)

The Brand Service is a stateless routing layer. It holds no business data itself — when an API call arrives, it resolves the tenant, enforces permissions, and routes the query to the correct tenant database.

5. Saga Pattern for Distributed Transactions

Operations that span multiple services (e.g. place order → reserve inventory → capture payment → dispatch) use the Saga pattern. Each step emits an event; compensating transactions roll back partial state if any step fails.

Infrastructure Stack

ComponentTechnology
Language / frameworkPHP 8.3 + Laravel 11
Admin panelsFilament 5
Reverse proxyNginx/OpenResty with dynamic routing
Event busApache Kafka
Cache / routingRedis
DatabasesMySQL (per-service, per-tenant)
Object storageMinIO (S3-compatible)
PaymentsStripe Connect
MonitoringPrometheus + Grafana + Loki + Promtail
ContainerisationDocker / Docker Compose / Kubernetes
CI/CDGitHub Actions
DeploymentAnsible on Debian EC2
IaCTerraform

Domain Routing

EnvironmentPatternExample
Production{service}.blindstrader.combrand.blindstrader.com
Staging{service}.stage.blindstrader.combrand.stage.blindstrader.com
Local{service}.blindstrader.testbrand.blindstrader.test
Docsdocs.blindstrader.com

The Nginx reverse proxy uses environment variable substitution (${DOMAIN}) to generate the correct server_name directives at runtime.

Deep Dives

Blindstrader Platform Documentation