AshBoundary is a Spark DSL extension for Ash domains.
It derives a boundary declaration from the domain DSL.
The boundary compiler enforces the declaration on each build.
Every option in the boundary block passes straight through to boundary, unchanged: deps,
exports, check, type, dirty_xrefs all mean exactly what they mean on a hand-written
use Boundary. AshBoundary computes one thing on top: exports gains the domain module itself
and every resource with at least one domain-level define.
Conventions
- The domain module is public.
- Each resource that exposes a code interface in the domain is public.
- Each module named in the
boundaryblock'sexportsis public. - All other modules in the domain's namespace are internal.
- Referencing another domain requires an explicit
boundarydep, subject to whatevercheckthat domain declares.
Installation
Add ash_boundary to the deps in mix.exs:
def deps do
[
{:ash_boundary, "~> 0.1.1"}
]
endAdd the :boundary compiler to the project configuration:
def project do
[
app: :my_app,
compilers: [:boundary] ++ Mix.compilers(),
# ...
]
endUsage
Add AshBoundary to the extensions of a domain. Declare dependencies on other domains, and any public module that is not a resource, in a boundary block:
defmodule MyApp.Blog do
use Ash.Domain, extensions: [AshBoundary]
boundary do
deps [MyApp.Accounts]
exports [MyApp.Blog.PostStatus]
end
resources do
resource MyApp.Blog.Post do
define :get_post, action: :read
define :update_post, action: :update
end
resource MyApp.Blog.Comment
end
endThis configuration has these effects:
MyApp.Blog.Postis public. All modules can reference it.MyApp.Blog.PostStatus, anAsh.Type.Enum, is public.MyApp.Blog.Commentis internal. Only modules in theMyApp.Blognamespace can reference it.MyApp.Blogcan depend on theMyApp.Accountsboundary only.
Every option of the boundary block is listed in the
DSL reference.
Examples
Three examples live in examples/, each its own Mix project:
01_exported_vs_internalhas two domains. It covers exported and internal resources, and a calculation that reads another domain's data through that domain's exported interface instead of a relationship.02_phoenix_liveviewis a Phoenix application over two domains. Its web layer reaches both through their exported interfaces and cannot callAsh.*.03_towerstacks thirteen domains over one PostgreSQL database in tiers, from shared infrastructure up to a JSON:API layer that composes the tiers below it. Two Hologram front ends sit above that, a public storefront and a role-gated operations console, each its own boundary.
Clarity
With clarity installed, each domain gains a "Boundary
Dependencies" tab holding a diagram of its deps, where clicking a node opens that domain.
The diagram stacks domains in tiers by their distance from the domains that depend on nothing, and leaves out any edge a longer path already implies.
Usage rules
The package ships a usage-rules.md for
usage_rules. List :ash_boundary in your
project's usage_rules and run mix usage_rules.sync:
def project do
[
usage_rules: [:ash_boundary],
# ...
]
endDocumentation
Docs for latest main is published at
mbuhot.github.io/ash_boundary.
Docs for versioned releases will be available at
hexdocs.pm/ash_boundary.
The DSL reference documents every option
of the boundary block.
See also the boundary docs.
License
MIT. See LICENSE.