-
Notifications
You must be signed in to change notification settings - Fork 41
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Update docs to explain layers, drop the beta warning for domain confi…
…guration files
- Loading branch information
Showing
3 changed files
with
110 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,7 +10,9 @@ This is the project-level configuration file which should be in the root of your | |
|
||
`modules` defines the modules in your project - [see details](#modules). | ||
|
||
`interfaces` defines the interfaces of modules in your project - [see details](#interfaces). | ||
`interfaces` defines the interfaces of modules in your project (optional) - [see details](#interfaces). | ||
|
||
`layers` defines the layers of modules in your project (optional) - [see details](#layers). | ||
|
||
`exclude` accepts a list of directory patterns to exclude from checking. These should be glob paths which match from the beginning of a given file path. For example: `project/*.tests` would match any path beginning with `project/` and ending with `.tests`. | ||
|
||
|
@@ -48,12 +50,19 @@ exact = true | |
ignore_type_checking_imports = true | ||
forbid_circular_dependencies = true | ||
|
||
layers = [ | ||
"ui", | ||
"commands", | ||
"core" | ||
] | ||
|
||
[[modules]] | ||
path = "tach" | ||
depends_on = [] | ||
|
||
[[modules]] | ||
path = "tach.__main__" | ||
layer = "ui" | ||
|
||
[[modules]] | ||
path = "tach.errors" | ||
|
@@ -63,6 +72,7 @@ utility = true | |
[[modules]] | ||
path = "tach.parsing" | ||
depends_on = ["tach", "tach.filesystem"] | ||
layer = "core" | ||
visibility = ["tach.check"] | ||
|
||
[[modules]] | ||
|
@@ -72,6 +82,7 @@ depends_on = [ | |
"tach.filesystem", | ||
"tach.parsing", | ||
] | ||
layer = "commands" | ||
|
||
[[interfaces]] | ||
expose = ["types.*"] | ||
|
@@ -136,6 +147,8 @@ More specifically: | |
- `expose`: a list of regex patterns which define the public interface | ||
- `from` (optional): a list of regex patterns which define the modules which adopt this interface | ||
|
||
[More details here.](../usage/interfaces) | ||
|
||
<Note> | ||
If an interface entry does not specify `from`, all modules will adopt the interface. | ||
</Note> | ||
|
@@ -144,6 +157,33 @@ If an interface entry does not specify `from`, all modules will adopt the interf | |
A module can match multiple interface entries - if an import matches _any_ of the entries, it will be considered valid. | ||
</Note> | ||
|
||
## Layers | ||
|
||
An ordered list of layers can be configured at the top level of `tach.toml`, | ||
and [modules](#modules) can each be assigned to a specific layer. | ||
|
||
```toml | ||
layers = [ | ||
"ui", | ||
"commands", | ||
"core" | ||
] | ||
|
||
[[modules]] | ||
path = "tach.check" | ||
layer = "commands" | ||
|
||
[[modules]] | ||
path = "tach.cache" | ||
layer = "core" | ||
``` | ||
|
||
The configuration above defines three layers, with `ui` being the highest layer, and `core` being the lowest layer. | ||
It also tags `tach.check` as a module in the `commands` layer, and `tach.cache` in `core`. | ||
|
||
[More details here.](../usage/layers) | ||
|
||
|
||
## The Root Module | ||
|
||
By default, Tach checks all of the source files beneath all of the configured [source roots](#source_roots), and will ignore dependencies which are not contained by [modules](#modules). | ||
|
@@ -221,7 +261,7 @@ To indicate this structure to Tach, set: | |
source_roots = ["backend"] | ||
``` | ||
|
||
in your `tach.toml`, or use [`tach mod`](commands#tach-mod) and mark the `backend` folder as the only source root. | ||
in your `tach.toml`, or use [`tach mod`](../usage/commands#tach-mod) and mark the `backend` folder as the only source root. | ||
|
||
### Example: Monorepo | ||
|
||
|
@@ -283,18 +323,12 @@ source_roots = [ | |
] | ||
``` | ||
|
||
in your `tach.toml`, or use [`tach mod`](commands#tach-mod) and mark the same folders as source root. | ||
in your `tach.toml`, or use [`tach mod`](../usage/commands#tach-mod) and mark the same folders as source root. | ||
|
||
In `tach.toml`, each entry in `source_roots` is interpreted as a relative path from the project root. | ||
|
||
## `tach.domain.toml` | ||
|
||
<Warning> | ||
This feature is in **beta**. The exact behavior and syntax of this configuration is subject to change and should not be considered stable. | ||
|
||
If it seems useful, we'd love to hear your feedback! E-mail us at [email protected] | ||
</Warning> | ||
|
||
Tach allows splitting your configuration into 'domains', or sub-folders of your project. | ||
You can define modules and interfaces in a `tach.domain.toml` file which lives right next to the module code itself. | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
--- | ||
title: Layers | ||
--- | ||
|
||
An ordered list of layers can be configured at the top level of [`tach.toml`](../usage/configuration#layers), | ||
and [modules](../usage/configuration#modules) can each be assigned to a specific layer. | ||
|
||
## How does it work? | ||
|
||
[Layered architecture](https://www.oreilly.com/library/view/software-architecture-patterns/9781491971437/ch01.html) is often | ||
an effective starting point for modularizing an application. | ||
|
||
The idea is straightforward: | ||
**Higher layers may import from lower layers, but lower layers may NOT import from higher layers.** | ||
|
||
Defining this architecture is more concise and flexible than specifying all module dependencies | ||
with `depends_on`, which makes it easier to adopt in an existing project. | ||
|
||
Tach allows defining and enforcing a layered architecture with any number of vertically-stacked layers. | ||
|
||
When a module is assigned to a layer, this module: | ||
- may freely depend on modules in **lower layers**, *without declaring these dependencies* | ||
- must explicitly declare dependencies in **its own layer** | ||
- may never depend on modules in **higher layers**, *even if they are declared* | ||
|
||
## Example | ||
|
||
We can use the Tach codebase itself as an example of a 3-tier layered architecture: | ||
|
||
```toml | ||
layers = [ | ||
"ui", | ||
"commands", | ||
"core" | ||
] | ||
|
||
[[modules]] | ||
path = "tach.check" | ||
layer = "commands" | ||
|
||
[[modules]] | ||
path = "tach.cache" | ||
depends_on = ["tach.filesystem"] | ||
layer = "core" | ||
|
||
[[modules]] | ||
path = "tach.filesystem" | ||
depends_on = [] | ||
layer = "core" | ||
``` | ||
|
||
In the configuration above, three layers are defined. | ||
They are similar to the classic `Presentation` - `Business Logic` - `Data` which are often found in web applications, | ||
but a bit different given that Tach is a CLI program. | ||
|
||
In Tach, the highest layer is `UI`, which includes code related to the CLI and other entrypoints to start the program. | ||
|
||
Just below this, the `Commands` layer contains high-level business logic which implements each of the CLI commands. | ||
|
||
At the bottom is the `Core` layer, which contains utilities, libraries, and broadly relevant data structures. | ||
|
||
Given this configuration, `tach.check` does not need to declare a dependency on `tach.cache` or `tach.filesystem` to use it, | ||
because the `Commands` layer is higher than the `Core` layer. | ||
|
||
However, `tach.cache` needs to explicitly declare its dependency on `tach.filesystem`, because they | ||
are *both* in the `Core` layer. |