Architecture Overview
OpenGate IAM is designed as a true microservices architecture — each service owns its own database, communicates asynchronously via Kafka, and is independently deployable.
On this page
System Diagram
OpenGate IAM
Architecture Overview · True Microservices · Event-Driven
Design Principles
1. Database per Service
Each microservice has its own PostgreSQL database. No shared tables, no cross-service JOINs. Data consistency is maintained through eventual consistency via Kafka events.
| Service | Database |
|---|---|
| auth-service | opengate_auth |
| user-service | opengate_users |
| realm-service | opengate_realms |
| rbac-service | opengate_rbac |
| client-service | opengate_clients |
| notification-service | opengate_notifications |
2. Gateway as Single Entry Point
All external traffic enters through the gateway on port 8080. Internal services are never exposed directly. The gateway handles CORS, routing, and request logging.
3. Stateless Services
All business logic services are stateless. State lives in:
- PostgreSQL — persistent entities (users, roles, clients, realms)
- Redis — ephemeral data (sessions, OTPs, rate limit counters, token blacklist)
- Kafka — event stream (audit trail)
Event Topology
| Topic | Producer | Consumers |
|---|---|---|
user.created | user-service | notification-service |
user.email_verified | user-service | — |
auth.login.success | auth-service | session-service |
auth.login.failure | auth-service | notification-service |
auth.logout | auth-service | session-service |
session.terminated | session-service | — |
mfa.otp_sent | mfa-service | — |
Security Model
Request
└─► Gateway (JWT validation via Spring Security OAuth2 Resource Server)
└─► Service (SecurityConfig: permitAll — gateway already verified)
Auth service exception
The auth-service uses Redis to store authorization codes and refresh tokens with TTLs — it is the only service with a meaningful Redis write dependency.
Multi-Tenancy (Realms)
Every resource is scoped to a realmName. The realm is extracted from the JWT realm claim or from the URL path (/realms/{realm}/...). Realm isolation is enforced at the database query level — every repository query includes realmName as a predicate.
/realms/{realmName}/...
│
▼
JWT claim: realm = "acme-corp"
│
▼
Repository: WHERE realm_name = 'acme-corp'