Atlas rethinks how we describe software architecture. This guide explains the key ideas.
In Atlas, an entity is:
The key insight: identity is for attachment; semantics are for reasoning.
In most systems, identity is spatial - bound to where things live:
src/
auth/
handlers.clj <- identity: src.auth.handlers
middleware.clj <- identity: src.auth.middleware
users/
handlers.clj <- identity: src.users.handlers
This creates problems:
Atlas uses semantic identity - what things mean:
;; This function's identity IS its semantic meaning
#{:atlas/execution-function
:domain/auth
:operation/validate
:tier/service}
Now you can query by any dimension:
:domain/auth:operation/validate:tier/service#{:domain/auth :operation/validate}A compound identity is a set of qualified keywords:
#{:atlas/execution-function :domain/cart :operation/create :tier/service :effect/write}
:atlas/execution-function:domain/cart, :tier/service:foo/bar not :barSets capture that identity is unordered and compositional:
;; These are the same entity
#{:atlas/execution-function :domain/auth :tier/service}
#{:tier/service :atlas/execution-function :domain/auth}
;; You can match on any subset
(query/find-by-aspect reg :domain/auth) ; finds it
(query/find-by-aspect reg :tier/service) ; finds it
(query/find-by-aspect reg #{:domain/auth :tier/service}) ; finds it
Entity types classify what kind of thing you're describing.
| Type | Purpose |
|---|---|
:atlas/execution-function | Business logic functions |
:atlas/interface-endpoint | API endpoints (HTTP, GraphQL, etc.) |
:atlas/structure-component | Infrastructure (DB, cache, external APIs) |
:atlas/data-schema | Data structure definitions |
:atlas/interface-protocol | Interfaces/contracts |
:atlas/business-pattern | Business patterns (saga, workflow) |
:atlas/governance-constraint | Compliance rules |
Define your own:
(registry/register!
:myapp/event
:atlas/type
#{}
{:registry-definition/keys [:event/topic :event/schema]})
;; Now use it
(registry/register!
:event/user-created
:myapp/event
#{:domain/users}
{:event/topic "users"
:event/schema [:map [:user-id :uuid]]})
Aspects are the semantic dimensions beyond entity type.
Domain - Business domains:
:domain/auth
:domain/users
:domain/billing
:domain/inventory
Tier - Architectural layers:
:tier/foundation ; infrastructure components
:tier/service ; business logic
:tier/api ; external interfaces
Operation - What action is performed:
:operation/create
:operation/read
:operation/update
:operation/delete
:operation/validate
Effect - Side effect type:
:effect/read
:effect/write
:effect/async
:effect/external
Use any qualified keyword. Common patterns:
;; HTTP methods
:http/get :http/post :http/delete
;; Feature flags
:feature/beta :feature/experimental
;; Compliance
:compliance/gdpr :compliance/hipaa
;; Team ownership
:team/platform :team/growth
Functions in Atlas declare their data flow explicitly.
What data the function needs to execute:
{:execution-function/context [:user/id :auth/token]}
What data the function produces:
{:execution-function/response [:user/data :user/roles]}
What components or functions it needs:
{:execution-function/deps #{:component/db :fn/validate-token}}
Atlas can analyze your entire system's data flow:
;; What produces :user/data?
(entity/trace-data-flow :user/data)
;; What consumes :auth/token?
(query/where @registry/registry
#(contains? (set (:execution-function/context %)) :auth/token))
Atlas enforces architectural boundaries through tiers.
┌─────────────────────────────────────┐
│ :tier/api │ HTTP endpoints, GraphQL
├─────────────────────────────────────┤
│ :tier/service │ Business logic functions
├─────────────────────────────────────┤
│ :tier/foundation │ DB, cache, external APIs
└─────────────────────────────────────┘
:tier/api:tier/foundationInvariants are architectural rules that Atlas validates.
Structural:
Data Flow:
Semantic:
(require '[atlas.invariant :as inv])
;; Check everything
(inv/check-all)
;; => {:valid? true :violations []}
;; Or specific checks
(inv/check [inv/invariant-deps-exist
inv/invariant-no-circular-deps])
;; Human readable
(println (inv/report))
Two styles:
DSL-based (declarative):
(require '[atlas.invariant.dsl :as dsl])
(dsl/dsl-axiom
:my/write-ops-have-audit
:error
"Write operations must have audit aspect"
{:op :dsl.op/entity-has-aspect :args :effect/write} ; when
{:op :dsl.op/entity-has-aspect :args :audit/enabled}) ; then
Function-based (imperative):
(dsl/fn-axiom
:my/no-orphan-schemas
:warning
"Data schemas should be referenced somewhere"
(fn [db]
;; Return seq of violations
[{:entity :schema/orphan :message "Not referenced"}]))
The registry is a global atom holding all entities:
;; The registry atom
atlas.registry/registry
;; Current state
@atlas.registry/registry
;; => {#{:atlas/... :domain/...} {:atlas/dev-id ... :prop ...}
;; #{:atlas/... :domain/...} {...}
;; ...}
;; Register
(registry/register! dev-id type aspects props)
;; Query
(query/find-by-aspect @registry/registry :domain/auth)
(query/find-by-dev-id @registry/registry :fn/login)
;; Lookup
(registry/fetch @registry/registry #{:atlas/... :domain/...})
(registry/identity-for :fn/login)
;; Introspect
(registry/summary) ; stats
(registry/clusters) ; group by namespace
(registry/registered-types) ; all entity types
Here's how these concepts combine in practice:
;; 1. A component (foundation tier)
(registry/register!
:component/stripe
:atlas/structure-component
#{:tier/foundation :domain/payments :integration/external}
{:component/provides [:stripe/charge :stripe/refund]})
;; 2. A function using it (service tier)
(registry/register!
:fn/process-payment
:atlas/execution-function
#{:tier/service :domain/payments :operation/create :effect/write}
{:execution-function/context [:payment/amount :payment/method :user/id]
:execution-function/response [:payment/id :payment/status]
:execution-function/deps #{:component/stripe}})
;; 3. An endpoint exposing it (api tier)
(registry/register!
:endpoint/create-payment
:atlas/interface-endpoint
#{:tier/api :domain/payments :http/post}
{:endpoint/context [:http/body :auth/user]
:endpoint/response [:http/json-response]
:endpoint/deps #{:fn/process-payment}})
;; Now query across dimensions
(query/find-by-aspect @registry/registry :domain/payments) ; all 3
(query/find-by-aspect @registry/registry :effect/write) ; just fn
(query/find-by-aspect @registry/registry :integration/external) ; just component
;; And validate
(inv/check-all) ; verifies deps exist, tiers respected, etc.
| Concept | What It Is |
|---|---|
| Compound Identity | Set of keywords = semantic meaning |
| Entity Type | Category (function, endpoint, component) |
| Aspects | Semantic dimensions (domain, tier, operation) |
| Data Flow | Context (in) + Response (out) + Deps |
| Tiers | Architectural layers with rules |
| Invariants | Validated architectural constraints |
| Registry | Global store of all entities |
The key insight: your architecture becomes data - queryable, validatable, and explorable.
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |