Annotated examples showing Atlas patterns. See test/app/ for complete implementations.
A minimal user management system demonstrating the three tiers.
(require '[atlas.registry :as registry])
(require '[atlas.ontology :as ont])
;; Initialize
(ont/register-entity-types!)
;; ═══════════════════════════════════════════════════════════════
;; FOUNDATION TIER - Infrastructure components
;; ═══════════════════════════════════════════════════════════════
(registry/register!
:component/user-db
:atlas/structure-component
#{:tier/foundation ; <- Required for components
:domain/users} ; <- Business domain
{:component/provides [:db/find-user
:db/save-user
:db/delete-user]})
;; ═══════════════════════════════════════════════════════════════
;; SERVICE TIER - Business logic functions
;; ═══════════════════════════════════════════════════════════════
(registry/register!
:fn/get-user
:atlas/execution-function
#{:tier/service ; <- Business logic layer
:domain/users
:operation/read} ; <- What kind of operation
{:execution-function/context [:user/id] ; Data IN
:execution-function/response [:user/data] ; Data OUT
:execution-function/deps #{:component/user-db}}) ; Dependencies
(registry/register!
:fn/create-user
:atlas/execution-function
#{:tier/service
:domain/users
:operation/create
:effect/write} ; <- Has side effects
{:execution-function/context [:user/email :user/name]
:execution-function/response [:user/id :user/data]
:execution-function/deps #{:component/user-db}})
(registry/register!
:fn/delete-user
:atlas/execution-function
#{:tier/service
:domain/users
:operation/delete
:effect/write}
{:execution-function/context [:user/id]
:execution-function/response [:operation/success?]
:execution-function/deps #{:component/user-db}})
;; ═══════════════════════════════════════════════════════════════
;; API TIER - HTTP endpoints
;; ═══════════════════════════════════════════════════════════════
(registry/register!
:endpoint/get-user
:atlas/interface-endpoint
#{:tier/api ; <- External interface
:domain/users
:http/get} ; <- HTTP method
{:endpoint/context [:http/path-params]
:endpoint/response [:http/json-response]
:endpoint/deps #{:fn/get-user}})
(registry/register!
:endpoint/create-user
:atlas/interface-endpoint
#{:tier/api
:domain/users
:http/post}
{:endpoint/context [:http/body]
:endpoint/response [:http/json-response]
:endpoint/deps #{:fn/create-user}})
(registry/register!
:endpoint/delete-user
:atlas/interface-endpoint
#{:tier/api
:domain/users
:http/delete}
{:endpoint/context [:http/path-params]
:endpoint/response [:http/status]
:endpoint/deps #{:fn/delete-user}})
Query this registry:
(require '[atlas.query :as query])
;; All user domain entities
(query/find-by-aspect @registry/registry :domain/users)
;; => 6 entities
;; Only write operations
(query/find-by-aspect @registry/registry :effect/write)
;; => [:fn/create-user :fn/delete-user]
;; Group by tier
(query/by-tier @registry/registry)
;; => {:tier/api [...], :tier/service [...], :tier/foundation [...]}
A system with multiple domains and cross-domain dependencies.
;; ═══════════════════════════════════════════════════════════════
;; AUTH DOMAIN
;; ═══════════════════════════════════════════════════════════════
(registry/register!
:component/oauth-provider
:atlas/structure-component
#{:tier/foundation
:domain/auth
:integration/external} ; <- External service
{:component/provides [:oauth/validate-token
:oauth/refresh-token]})
(registry/register!
:fn/validate-session
:atlas/execution-function
#{:tier/service
:domain/auth
:operation/validate}
{:execution-function/context [:auth/token]
:execution-function/response [:auth/valid? :auth/user-id]
:execution-function/deps #{:component/oauth-provider}})
;; ═══════════════════════════════════════════════════════════════
;; ORDERS DOMAIN (depends on auth)
;; ═══════════════════════════════════════════════════════════════
(registry/register!
:component/order-db
:atlas/structure-component
#{:tier/foundation :domain/orders}
{:component/provides [:db/create-order :db/get-order]})
(registry/register!
:fn/create-order
:atlas/execution-function
#{:tier/service
:domain/orders
:operation/create
:effect/write}
{:execution-function/context [:auth/user-id ; <- From auth domain!
:order/items
:order/shipping-address]
:execution-function/response [:order/id :order/total]
:execution-function/deps #{:component/order-db
:fn/validate-session}}) ; <- Cross-domain dep
(registry/register!
:endpoint/create-order
:atlas/interface-endpoint
#{:tier/api
:domain/orders
:http/post}
{:endpoint/context [:http/body :http/headers] ; auth token in headers
:endpoint/response [:http/json-response]
:endpoint/deps #{:fn/create-order}})
Analyze cross-domain coupling:
;; What domains does orders depend on?
(->> (query/find-by-aspect @registry/registry :domain/orders)
(mapcat #(-> % :props :execution-function/deps))
(map registry/identity-for)
(mapcat #(filter (fn [k] (= "domain" (namespace k))) %))
(into #{}))
;; => #{:domain/orders :domain/auth}
Defining data structures with semantic annotations.
;; ═══════════════════════════════════════════════════════════════
;; DATA SCHEMAS
;; ═══════════════════════════════════════════════════════════════
(registry/register!
:schema/user
:atlas/data-schema
#{:domain/users
:data/entity} ; <- It's an entity (has ID)
{:schema/fields [:user/id
:user/email
:user/name
:user/created-at]})
(registry/register!
:schema/user-credentials
:atlas/data-schema
#{:domain/auth
:data/sensitive ; <- Contains PII
:compliance/gdpr} ; <- GDPR relevant
{:schema/fields [:auth/email
:auth/password-hash
:auth/mfa-secret]})
(registry/register!
:schema/order
:atlas/data-schema
#{:domain/orders
:data/entity
:data/aggregate} ; <- Aggregate root
{:schema/fields [:order/id
:order/user-id
:order/items ; <- nested
:order/total
:order/status]})
(registry/register!
:schema/order-item
:atlas/data-schema
#{:domain/orders
:data/value-object} ; <- No independent ID
{:schema/fields [:item/product-id
:item/quantity
:item/price]})
Query schemas:
;; Find all sensitive data
(query/find-by-aspect @registry/registry :data/sensitive)
;; => [:schema/user-credentials]
;; Find GDPR-relevant schemas
(query/find-by-aspect @registry/registry :compliance/gdpr)
Defining interfaces and validating conformance.
;; ═══════════════════════════════════════════════════════════════
;; PROTOCOL DEFINITION
;; ═══════════════════════════════════════════════════════════════
(registry/register!
:protocol/payment-provider
:atlas/interface-protocol
#{:domain/payments}
{:protocol/functions [:payment/charge
:payment/refund
:payment/get-status]})
;; ═══════════════════════════════════════════════════════════════
;; IMPLEMENTATION (Stripe)
;; ═══════════════════════════════════════════════════════════════
(registry/register!
:component/stripe
:atlas/structure-component
#{:tier/foundation
:domain/payments
:integration/external}
{:component/implements :protocol/payment-provider ; <- Declares conformance
:component/provides [:payment/charge ; <- Must match protocol
:payment/refund
:payment/get-status]})
;; ═══════════════════════════════════════════════════════════════
;; ALTERNATIVE IMPLEMENTATION (PayPal)
;; ═══════════════════════════════════════════════════════════════
(registry/register!
:component/paypal
:atlas/structure-component
#{:tier/foundation
:domain/payments
:integration/external}
{:component/implements :protocol/payment-provider
:component/provides [:payment/charge
:payment/refund
:payment/get-status]})
Invariants will verify that implementations provide all protocol functions.
Extending Atlas with your own entity types.
;; ═══════════════════════════════════════════════════════════════
;; DEFINE CUSTOM TYPE
;; ═══════════════════════════════════════════════════════════════
(registry/register!
:myapp/event
:atlas/type ; <- Register as a type
#{}
{:registry-definition/keys [:event/topic
:event/schema
:event/producers
:event/consumers]})
;; ═══════════════════════════════════════════════════════════════
;; USE CUSTOM TYPE
;; ═══════════════════════════════════════════════════════════════
(registry/register!
:event/user-created
:myapp/event ; <- Use your custom type
#{:domain/users}
{:event/topic "users.created"
:event/schema :schema/user
:event/producers #{:fn/create-user}
:event/consumers #{:fn/send-welcome-email
:fn/update-analytics}})
(registry/register!
:event/order-placed
:myapp/event
#{:domain/orders}
{:event/topic "orders.placed"
:event/schema :schema/order
:event/producers #{:fn/create-order}
:event/consumers #{:fn/process-payment
:fn/notify-warehouse}})
Query your custom types:
;; Find all events
(query/where @registry/registry
#(= :myapp/event (registry/entity-type (:identity %))))
;; Find events with specific consumer
(query/where @registry/registry
#(contains? (-> % :props :event/consumers) :fn/process-payment))
Separating test implementations from production.
;; ═══════════════════════════════════════════════════════════════
;; PRODUCTION COMPONENT
;; ═══════════════════════════════════════════════════════════════
(registry/register!
:component/email-service
:atlas/structure-component
#{:tier/foundation
:domain/notifications
:integration/external}
{:component/provides [:email/send]})
;; ═══════════════════════════════════════════════════════════════
;; TEST IMPLEMENTATION (separate entity!)
;; ═══════════════════════════════════════════════════════════════
(registry/register!
:component/email-service-mock
:atlas/structure-component
#{:tier/foundation
:domain/notifications
:test/mock} ; <- Marked as test mock
{:component/provides [:email/send]
:component/replaces :component/email-service})
Filter in production vs test:
;; Production: exclude test mocks
(query/where @registry/registry
#(not (contains? (:identity %) :test/mock)))
;; Test: find all mocks
(query/find-by-aspect @registry/registry :test/mock)
| Pattern | Key Aspects |
|---|---|
| CRUD Service | :operation/create, :operation/read, etc. |
| Write Effects | :effect/write |
| External Integration | :integration/external |
| Sensitive Data | :data/sensitive, :compliance/gdpr |
| Test Mocks | :test/mock |
| Custom Types | Register with :atlas/type |
For complete, runnable examples see:
test/app/calendar_availability.clj - OAuth, users, calendar eventstest/app/cart.clj - Shopping cart with sessionstest/app/pet_shop.clj - Multi-domain with external integrationsCan 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 |