@@ -4,20 +4,141 @@ title: Dependency Rules
4
4
displayed_sidebar : tutorialSidebar
5
5
---
6
6
7
- Sheriff provides access rules.
7
+ ## Introduction
8
8
9
- To define access rules, run ` npx sheriff init ` in your project's root folder. This creates a _ sheriff.config.ts_ file, where you can define the tags and dependency rules.
9
+ Dependency rules determine which modules can access other modules. Since managing dependencies on a per-module basis
10
+ doesn't scale well, Sheriff utilizes tags to group modules together. Dependency rules are then defined based on these
11
+ tags.
10
12
11
- The initial ` sheriff.config.ts ` doesn't have any restrictions in terms of dependency rules.
13
+ Each tag specifies a list of other tags it can access. To maintain clarity, it’s best practice to categorize tags into
14
+ two groups: one for defining the module's domain/scope and another for defining the module's type.
15
+
16
+ For instance, if an application includes a customer domain and a holiday domain, these would define the domain tags.
17
+
18
+ Within a domain, you might have different modules distinguished by type tags. For example, one module might contain
19
+ smart components, another might have dumb components, and another might handle logic.
20
+
21
+ Domain tags could be ` domain:customer ` and ` domain:holiday ` . Type tags could be ` type:feature ` (for smart components),
22
+ ` type:ui ` (for dumb components), or ` type:data ` (for logic).
23
+
24
+ Each module should have both a domain tag and a type tag. For example, a module containing smart components for
25
+ customers would be tagged with ` domain:customer ` and ` type:feature ` . A module in the same domain but containing UI
26
+ components would be tagged with ` domain:customer ` and ` type:ui ` .
27
+
28
+ Dependency rules specify that a module tagged with ` domain:customer ` can only access modules with the same domain tag.
29
+ Additionally, a module tagged with ` type:feature ` can access modules tagged with ` type:ui ` and ` type:data ` .
30
+
31
+ When a module containing smart components needs to access a dumb component, Sheriff retrieves the tags for both modules.
32
+ It then checks each tag of the smart component module to determine if it is permitted to access the corresponding tags
33
+ of the dumb component module.
34
+
35
+ ``` mermaid
36
+ stateDiagram-v2
37
+ [*] --> RequestAccessToModule
38
+ RequestAccessToModule --> GetFromAndToTags
39
+ GetFromAndToTags --> IterateOverFromTags
40
+ IterateOverFromTags --> HasFromTagAccessToAnyToTag
41
+ HasFromTagAccessToAnyToTag --> AccessDenied: No
42
+ HasFromTagAccessToAnyToTag --> MoreFromTagsAvailable: Yes
43
+ MoreFromTagsAvailable --> AccessGranted: No
44
+ MoreFromTagsAvailable --> IterateOverFromTags: Yes
45
+ AccessGranted --> [*]
46
+ AccessDenied --> [*]
47
+
48
+ ```
49
+
50
+ Since both modules share the same domain, access is allowed based on the domain tag. Additionally, because "type:
51
+ feature" is allowed to access "type: ui ", all tags are cleared, and access is granted.
52
+
53
+ ``` mermaid
54
+ flowchart LR
55
+ subgraph "smart components"
56
+ direction TB
57
+ dc1[domain:customer]
58
+ sc1[type:feature]
59
+ end
60
+
61
+ subgraph "dumb components"
62
+ direction TB
63
+ dc2[domain:customer]
64
+ sc2[type:ui]
65
+ end
66
+
67
+ sc1 --> sc2
68
+ dc1 --> dc2
69
+ linkStyle 0 stroke-width: 2px, fill: green, stroke: green;
70
+ linkStyle 1 stroke-width: 2px, fill: green, stroke: green;
71
+ ```
72
+
73
+ Conversely, if a dumb component tries to access a smart component, access would be denied based on the type tag.
74
+
75
+ ``` mermaid
76
+ flowchart LR
77
+ subgraph "dumb components"
78
+ direction TB
79
+ dc1[domain:customer]
80
+ sc1[type:ui]
81
+ end
82
+
83
+ subgraph "smart components"
84
+ direction TB
85
+ dc2[domain:customer]
86
+ sc2[type:feature]
87
+ end
88
+
89
+ sc1 --> sc2
90
+ dc1 --> dc2
91
+ linkStyle 0 stroke-width: 2px, fill: green, stroke: red;
92
+ linkStyle 1 stroke-width: 2px, fill: green, stroke: green;
93
+ ```
94
+
95
+ If a smart component from the customer domain tries to access a dumb component from another domain, access would be
96
+ denied due to the domain tag.
97
+
98
+ ``` mermaid
99
+ flowchart LR
100
+ subgraph "Customers"
101
+ subgraph "smart components"
102
+ direction TB
103
+ dc1[domain:customer]
104
+ sc1[type:feature]
105
+ end
106
+ end
107
+
108
+ subgraph Holidays
109
+ subgraph "dumb components"
110
+ direction TB
111
+ dc2[domain:holiday]
112
+ sc2[type:ui]
113
+ end
114
+ end
115
+
116
+ sc1 --> sc2
117
+ dc1 --> dc2
118
+ linkStyle 0 stroke-width: 2px, fill: green, stroke: green;
119
+ linkStyle 1 stroke-width: 2px, fill: green, stroke: red;
120
+ ```
12
121
13
122
## Automatic Tagging
14
123
15
- By default, an untagged module has the tag "noTag". All files which are not part of a module are
16
- assigned to the "root" module and therefore have the tag "root".
124
+ Initially, you don't need to assign tags to modules manually.
125
+
126
+ Any module that remains untagged is automatically assigned the ` noTag ` tag.
127
+
128
+ All files that are not part of a specific module are assigned to the ` root ` module and therefore receive the ` root ` tag.
129
+
130
+ However, it is essential to set up the dependency rules. Specifically, you must allow the [ ` root ` tag] ( #the-root-tag ) (
131
+ i.e., the root module) to access all other untagged modules.
17
132
18
- Dependency rules operate on those tags.
133
+ This configuration is done by setting the ` depRules ` in the _ sheriff.config.ts_ file. The ` depRules ` is an object
134
+ literal where each key represents the from tag and its value specifies the tags it can access.
19
135
20
- Here's an example of a _ sheriff.config.ts_ file with auto-tagged modules:
136
+ Initially, all modules can access each other. As a result, every ` noTag ` module can access other ` noTag ` modules as well
137
+ as those tagged with ` root ` .
138
+
139
+ If you use the [ CLI] ( ./cli ) to initialize Sheriff for the first time, this configuration will be set up automatically.
140
+
141
+ Here's an example configuration in _ sheriff.config.ts_ :
21
142
22
143
``` typescript
23
144
import { SheriffConfig } from ' @softarc/sheriff-core' ;
@@ -30,12 +151,11 @@ export const sheriffConfig: SheriffConfig = {
30
151
};
31
152
```
32
153
33
- The configuration allows every module with tag "noTag" to access any other module with tag "noTag"
34
- and [ "root"] ( #the-root-tag ) .
154
+ This approach is recommended for existing projects, as it allows for the incremental introduction of Sheriff.
35
155
36
- This is the recommendation for existing projects and allows an incremental introduction of Sheriff .
156
+ If you are starting a new project, you can skip this step and proceed directly to [ manual tagging ] ( #manual-tagging ) .
37
157
38
- If you start from scratch, you should go with [ manual tagging ] ( #manual-tagging ) .
158
+ ---
39
159
40
160
To disable automatic tagging, set ` autoTagging ` to ` false ` :
41
161
@@ -52,7 +172,7 @@ export const sheriffConfig: SheriffConfig = {
52
172
53
173
## The ` root ` Tag
54
174
55
- Let's say we have the following directory structure:
175
+ Consider the following directory structure:
56
176
57
177
<pre >
58
178
src/app
@@ -72,9 +192,10 @@ src/app
72
192
│ ├── footer.component.ts
73
193
</pre >
74
194
75
- _ src/app/holidays/data_ and _ src/app/holidays/feature_ are modules. All other files are part of the root module which
76
- is tagged with "root". Sheriff assigns the tag "root" automatically. You cannot change it and "root" doesn't have an
77
- _ index.ts_ . [ By default] ( ./integration ) , it is not possible to import from the root module.
195
+ The directories _ src/app/holidays/data_ and _ src/app/holidays/feature_ are considered modules. All other files are part
196
+ of the root module, which is automatically tagged with ` root ` by Sheriff. This tag cannot be changed, and the root
197
+ module does not include an _ index.ts_ file. [ By default] ( ./integration ) , importing from the root module is not
198
+ permitted.
78
199
79
200
``` mermaid
80
201
flowchart LR
@@ -105,7 +226,10 @@ flowchart LR
105
226
106
227
## Manual Tagging
107
228
108
- The following snippet shows a configuration where four directories are assigned to a domain and to a module type:
229
+ To assign tags manually, you need to provide a ` tagging ` object in the _ sheriff.config.ts_ file. The keys in this object
230
+ represent the module directories, and the corresponding values are the tags assigned to those modules.
231
+
232
+ The following snippet demonstrates a configuration where four directories are assigned both a domain and a module type:
109
233
110
234
``` typescript
111
235
import { SheriffConfig } from ' @softarc/sheriff-core' ;
@@ -121,11 +245,12 @@ export const sheriffConfig: SheriffConfig = {
121
245
};
122
246
```
123
247
124
- With " domain:_ " and " type:_ " , we have two dimensions which allows us to define the following rules:
248
+ By using ` domain:_ ` and ` type:_ ` , we establish two dimensions that allow us to define the following rules:
125
249
126
- 1 . A module can only depend on modules of the same domain
127
- 2 . A module of "type: feature " can depend on "type: data " but not the other way around
128
- 3 . "root" can depend on a module of "type: feature " and both domains.
250
+ 1 . A module can only depend on other modules within the same domain.
251
+ 2 . A module tagged as ` type:feature ` can depend on ` type:data ` , but the reverse is not allowed.
252
+ 3 . The ` root ` module can depend on modules tagged as ` type:feature ` . Since the root module only has the ` root ` tag,
253
+ there is no need to include domain tags.
129
254
130
255
``` typescript
131
256
import { SheriffConfig } from ' @softarc/sheriff-core' ;
@@ -142,18 +267,19 @@ export const sheriffConfig: SheriffConfig = {
142
267
' domain:holidays' : [' domain:holidays' ], // Rule 1
143
268
' domain:customers' : [' domain:customers' ], // Rule 1
144
269
' type:feature' : ' type:data' , // Rule 2
145
- root: [ ' type:feature' , ' domain:holidays ' , ' domain:customers ' ] , // Rule 3
270
+ root: ' type:feature' , // Rule 3
146
271
},
147
272
};
148
273
```
149
274
150
- If those roles are violated, a linting error is thrown :
275
+ If these rules are violated, a linting error will be triggered :
151
276
152
277
<img width =" 1512 " alt =" Screenshot 2023-06-13 at 17 50 41 " src =" /img/dependency-rules-1.png " ></img >
153
278
154
- For existing projects, you want to tag modules and define dependency rules incrementally.
279
+ For existing projects, it is recommended to tag modules and define dependency rules incrementally.
155
280
156
- If you only want to tag modules from "holidays" and leave the rest auto-tagged, you can do so:
281
+ If you prefer to only tag modules within the "holidays" directory and leave the rest of the modules auto-tagged, you can
282
+ do so:
157
283
158
284
``` typescript
159
285
import { SheriffConfig } from ' @softarc/sheriff-core' ;
@@ -166,14 +292,14 @@ export const sheriffConfig: SheriffConfig = {
166
292
depRules: {
167
293
' domain:holidays' : [' domain:holidays' , ' noTag' ],
168
294
' type:feature' : [' type:data' , ' noTag' ],
169
- root: [' type:feature' , ' domain:holidays ' , ' noTag' ],
295
+ root: [' type:feature' , ' noTag' ],
170
296
noTag: [' noTag' , ' root' ],
171
297
},
172
298
};
173
299
```
174
300
175
- All modules in the directory "customers" have the tag " noTag" . Be aware, that every module from "domain : holidays " can
176
- now depend on any module from directory "customers" but not vice versa .
301
+ All modules in the "customers" directory are assigned the ` noTag ` tag . Be aware that this setup allows any module from
302
+ ` domain:holidays ` to depend on modules within the "customers" directory, but the reverse is not permitted .
177
303
178
304
## Nested Paths
179
305
@@ -199,7 +325,7 @@ export const sheriffConfig: SheriffConfig = {
199
325
' domain:holidays' : [' domain:holidays' ],
200
326
' domain:customers' : [' domain:customers' ],
201
327
' type:feature' : ' type:data' ,
202
- root: [' type:feature' , ' domain:holidays ' , ' domain:customers ' ],
328
+ root: [' type:feature' ],
203
329
},
204
330
};
205
331
```
@@ -226,7 +352,7 @@ export const sheriffConfig: SheriffConfig = {
226
352
' domain:holidays' : [' domain:holidays' ],
227
353
' domain:customers' : [' domain:customers' ],
228
354
' type:feature' : ' type:data' ,
229
- root: [' type:feature' , ' domain:holidays ' , ' domain:customers ' ],
355
+ root: [' type:feature' ],
230
356
},
231
357
};
232
358
```
@@ -237,15 +363,14 @@ We can use placeholders on all levels. Our configuration is now more concise.
237
363
import { SheriffConfig } from ' @softarc/sheriff-core' ;
238
364
239
365
export const sheriffConfig: SheriffConfig = {
240
- version: 1 ,
241
366
tagging: {
242
367
' src/app/<domain>/<type>' : [' domain:<domain>' , ' type:<type>' ],
243
368
},
244
369
depRules: {
245
370
' domain:holidays' : [' domain:holidays' ],
246
371
' domain:customers' : [' domain:customers' ],
247
372
' type:feature' : ' type:data' ,
248
- root: [' type:feature' , ' domain:holidays ' , ' domain:customers ' ],
373
+ root: [' type:feature' ],
249
374
},
250
375
};
251
376
```
@@ -265,12 +390,12 @@ export const sheriffConfig: SheriffConfig = {
265
390
depRules: {
266
391
' domain:*' : ({ from , to }) => from === to ,
267
392
' type:feature' : ' type:data' ,
268
- root: [' type:feature' , ({ to }) => to . startsWith ( ' domain: ' ) ],
393
+ root: [' type:feature' ],
269
394
},
270
395
};
271
396
```
272
397
273
- or
398
+ or use ` sameTag ` , which is a pre-defined function.
274
399
275
400
``` typescript
276
401
import { sameTag , SheriffConfig } from ' @softarc/sheriff-core' ;
@@ -283,7 +408,7 @@ export const sheriffConfig: SheriffConfig = {
283
408
depRules: {
284
409
' domain:*' : [sameTag , ' shared' ],
285
410
' type:feature' : ' type:data' ,
286
- root: [' type:feature' , ({ to }) => to . startsWith ( ' domain: ' ) ],
411
+ root: [' type:feature' ],
287
412
},
288
413
};
289
414
```
0 commit comments