<!--
SPDX-FileCopyrightText: 2019 ash contributors <https://github.com/ash-project/ash/graphs/contributors>
SPDX-License-Identifier: MIT
-->
# Domains
Domains serve three primary purposes:
1. They group related resources together, providing organization and structure to your project.
2. They allow you to define a centralized [code interface](/documentation/topics/resources/code-interfaces.md)
3. They allow you to configure certain cross-cutting concerns of those resources in a single place.
If you are familiar with a [Phoenix Context](https://hexdocs.pm/phoenix/contexts.html), you can think of a domain as the Ash equivalent.
## Application Configuration (`:ash_domains`)
Ash expects you to list your domain modules in your application config:
```elixir
config :my_app, :ash_domains, [MyApp.Tweets, MyApp.Billing]
```
This configuration is used for:
- Mix tasks and tooling that need to load all domains (e.g. diagrams, livebooks, policy charts)
- Compile-time validation that domains and resources are registered (the warnings shown by `use Ash.Domain` and `use Ash.Resource`)
If you see warnings about missing domains or resources, it usually means this list is incomplete.
You can add your domain modules here to resolve those warnings, or disable the validations if you
prefer to manage it manually.
## Grouping Resources
In an `Ash.Domain`, you will typically see something like this:
```elixir
defmodule MyApp.Tweets do
use Ash.Domain
resources do
resource MyApp.Tweets.Tweet
resource MyApp.Tweets.Comment
end
end
```
With this definition, you can do things like placing all of these resources into a GraphQL Api with AshGraphql. You'd see a line like this:
```elixir
use AshGraphql, domains: [MyApp.Tweets]
```
## Centralized [Code Interface](/documentation/topics/resources/code-interfaces.md)
Working with our domain & resources in code *can* be done the long form way, by building changesets/queries/action inputs and calling the relevant function in `Ash`. However, we generally want to expose a well defined code API for working with our resources. This makes our code much clearer, and gives us nice things like auto complete and inline documentation.
```elixir
defmodule MyApp.Tweets do
use Ash.Domain
resources do
resource MyApp.Tweets.Tweet do
# define a function called `tweet` that uses
# the `:create` action on MyApp.Tweets.Tweet
define :tweet, action: :create, args: [:text]
end
resource MyApp.Tweets.Comment do
# define a function called `comment` that uses
# the `:create` action on MyApp.Tweets.Comment
define :comment, action: :create, args: [:tweet_id, :text]
end
end
end
```
With these definitions, we can now do things like this:
```elixir
tweet = MyApp.Tweets.tweet!("My first tweet!", actor: user1)
comment = MyApp.Tweets.comment!(tweet.id, "What a cool tweet!", actor: user2)
```
## Configuring Cross-cutting Concerns
### Built in configuration
`Ash.Domain` comes with a number of built-in configuration options. See `d:Ash.Domain` for more.
For example:
```elixir
defmodule MyApp.Tweets do
use Ash.Domain
resources do
resource MyApp.Tweets.Tweet
resource MyApp.Tweets.Comment
end
execution do
# raise the default timeout for all actions in this domain from 30s to 60s
timeout :timer.seconds(60)
end
authorization do
# disable using the authorize?: false flag when calling actions
authorize :always
end
end
```
### Extensions
Extensions will often come with "domain extensions" to allow you to configure the behavior of all resources within a domain, as it pertains to that extension. For example:
```elixir
defmodule MyApp.Tweets do
use Ash.Domain,
extensions: [AshGraphql.Domain]
graphql do
# skip authorization for these resources
authorize? false
end
resources do
resource MyApp.Tweets.Tweet
resource MyApp.Tweets.Comment
end
end
```
### Policies
You can also use `Ash.Policy.Authorizer` on your domains. This allows you to add policies that apply to *all* actions using this domain. For example:
```elixir
defmodule MyApp.Tweets do
use Ash.Domain,
extensions: [Ash.Policy.Authorizer]
resources do
resource MyApp.Tweets.Tweet
resource MyApp.Tweets.Comment
end
policies do
# add a bypass up front to allow administrators to do whatever they want
bypass actor_attribute_equals(:is_admin, true) do
authorize_if always()
end
# forbid all access from disabled users
policy actor_attribute_equals(:disabled, true) do
forbid_if always()
end
end
end
```