AtriumX Core – Architecture & Rationale
1. Context & Motivation
Section titled “1. Context & Motivation”AtriumX exists because the current ILS landscape often presents a choice between two distinct approaches:
- Traditional Systems (e.g., Koha): Functionally comprehensive and battle-tested, but their monolithic architecture and long history can make it challenging for teams to adopt modern, cloud-native development workflows.
- Large-Scale Modular Systems (e.g., FOLIO): Architecturally ambitious and highly flexible, but the microservices-first approach (incorporating technologies like Kafka and Okapi) can introduce substantial operational complexity and infrastructure costs.
AtriumX explicitly targets the missing middle:
A modern, opinionated, developer‑friendly ILS core that is simple to run, hard to misuse, and easy to extend.
2. Non‑Goals (Important)
Section titled “2. Non‑Goals (Important)”To maintain a lean and focused codebase, AtriumX does not aim to:
- Be a microservice-heavy ecosystem
- Support every library workflow on day one
- Inherit traditional architecture constraints (such as MARC-centricity in the core or tightly coupled database logic)
- Optimize for “any stack” contributors
AtriumX optimizes for:
- Correctness over convenience
- Explicit architecture over magic
- Long‑term maintainability over short‑term speed
3. High‑Level Architecture
Section titled “3. High‑Level Architecture”AtriumX follows a Modular Monolith with Hexagonal (Ports & Adapters) architecture.
┌─────────────────────────────┐ │ Frontend UI │ │ (Vue / React, TypeScript) │ └──────────────▲──────────────┘ │ JSON / REST ┌───────────────┴───────────────┐ │ API Adapters │ │ (Spring MVC, SIP2, etc.) │ └───────────────▲───────────────┘ │ Ports ┌───────────────────────┴────────────────────────┐ │ Core │ │ Domain + Application Services (Pure Kotlin) │ │ │ │ Catalog • Patrons • Circulation • Auth (later)│ └───────────────▲───────────────────▲────────────┘ │ │ Outbound Ports Outbound Ports │ │ ┌───────────────┴───────┐ ┌───────┴───────────┐ │ Persistence Adapter │ │ Search Adapter │ │ (PostgreSQL + jOOQ) │ │ (OpenSearch/ES) │ └───────────────────────┘ └───────────────────┘Key idea:
All business rules live in the Core. Everything else is replaceable.
4. Repository & Module Structure
Section titled “4. Repository & Module Structure”Single repository, multiple Gradle modules:
atriumx-core/├── app/ # Spring Boot runtime (The Glue)│ ├── build.gradle.kts # Depends on :core and :adapters│ └── src/main/.../AtriumXApp.kt│├── core/ # The Domain (Pure Kotlin)│ ├── build.gradle.kts # Depends on NOTHING (standard libs only)│ └── src/main/kotlin/org/atriumx/core/│ ├── catalog/│ │ ├── Item.kt # Data Class│ │ ├── ItemRepository.kt # Interface (Port)│ │ └── CatalogService.kt # Business Logic│ ├── circulation/│ └── shared/ # Common Exceptions / Value Objects│└── adapters/ ├── api-rest/ # The Input Port │ ├── build.gradle.kts # Depends on :core │ └── src/main/.../catalog/ │ ├── ItemController.kt │ └── dto/ │ ├── persistence-jooq/ # The Output Port │ ├── build.gradle.kts # Depends on :core, postgres, jooq │ └── src/main/.../catalog/ │ └── JooqItemRepository.kt # Implements core.ItemRepository │ └── search-opensearch/ # Secondary Output PortDependency Direction (Strict)
Section titled “Dependency Direction (Strict)”core → depends on NOTHINGadapters → depend on coreapp → depends on adaptersReverse dependencies are forbidden.
This is enforced by:
- Module boundaries
- Gradle dependencies
- Code reviews
5. Core Layer (The Heart)
Section titled “5. Core Layer (The Heart)”What belongs here
Section titled “What belongs here”- Aggregates (e.g.
Loan,Item,Patron) - Domain invariants (loan rules, limits, states)
- Application services (use‑cases)
- Domain events
- Interfaces (ports) for external concerns
What does NOT belong here
Section titled “What does NOT belong here”- Spring annotations
- HTTP concepts
- SQL
- JSON
- Authentication frameworks
The Core must:
- Compile without Spring
- Be unit‑testable without mocks of frameworks
- Remain stable even if adapters change
6. Adapters Layer (Delivery & Infrastructure)
Section titled “6. Adapters Layer (Delivery & Infrastructure)”API Adapter (adapters/api-rest)
Section titled “API Adapter (adapters/api-rest)”Responsibilities:
- Translate HTTP → application commands
- Validate request DTOs
- Map domain errors → HTTP responses
Characteristics:
- Uses Spring MVC
- Imports Spring Boot BOM
- Contains no business logic
Controllers are thin by design.
Persistence Adapter (adapters/persistence-jooq)
Section titled “Persistence Adapter (adapters/persistence-jooq)”Responsibilities:
- Implement repository ports
- Map domain objects ↔ relational schema
Why jOOQ (not JPA):
- Schema‑first
- Compile‑time SQL safety
- No hidden lazy loading
- Predictable performance
This is a deliberate choice to prioritize explicit control over ORM-driven abstractions.
Search Adapter (adapters/search-opensearch)
Section titled “Search Adapter (adapters/search-opensearch)”Responsibilities:
- Consume domain events
- Maintain search indexes
Search is:
- Eventually consistent
- Explicitly decoupled from core logic
No domain rule ever depends on search results.
7. App Module (The Runtime Shell)
Section titled “7. App Module (The Runtime Shell)”The app module:
- Is the only Spring Boot application
- Owns:
- Embedded server
- Bean wiring
- Configuration
Why isolate it:
- Prevent Spring leakage into core
- Keep startup concerns localized
- Allow alternative runtimes later
8. Frontend Strategy
Section titled “8. Frontend Strategy”Frontend is a separate project:
- TypeScript + Vue or React
- Communicates only via REST
- No shared code, no server‑side rendering assumptions
The backend must be fully usable via:
- Postman
- curl
- API clients
UI is a consumer, not a co‑author.
9. Phased Delivery Plan
Section titled “9. Phased Delivery Plan”- Catalog
- Patrons
- Circulation
- REST only
Stage 2
Section titled “Stage 2”- Authentication & Authorization
- SIP2
- Roles & policies
Later / Optional
Section titled “Later / Optional”- Acquisitions
- Serials (low priority, high complexity)
10. Why This Architecture
Section titled “10. Why This Architecture”This approach:
- Keeps up with the modern design principles
- Avoids operational overhead
- Tries to embrace the cloud-native practices
- Attracts the right contributors for the stack
- Keeps the system understandable after 5-10 years
It is boring on purpose.
Final Principle
Section titled “Final Principle”If a developer cannot explain where a piece of code belongs, it doesn’t belong.
AtriumX intentionally trades short-term velocity for long-term architectural stability.