Skip to content

Commit 945ba61

Browse files
committed
consolidation for version 1.1.0
2 parents 3b1d5b1 + 92810f3 commit 945ba61

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+3295
-364
lines changed

libs/ddd/README.md

Lines changed: 17 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ The generated access restrictions prevent unwanted access between libraries resp
1313
- 🗺️ Generating domains with domain libraries including a facades, models, and data services
1414
- ⚙️ Generating feature libraries including a feature components using the facades
1515
- 🙅‍♂️ Adding linting rules for access restrictions between domains as proposed by Nrwl
16-
- 🙅‍♀️ Adding linting rules for access restrictions between layers as proposed by Nrwl
16+
- 🙅‍♀️ Adding linting rules for access restrictions between layers as proposed by Nrwl (supports tslint and eslint)
17+
- 🔥 Optionally generates skeleton for NGRX and integrates it into the DDD design (``--ngrx`` switch)
1718

1819
## Usage
1920

@@ -26,49 +27,22 @@ ng add @angular-architects/ddd
2627
Add domains and features manually:
2728

2829
```
29-
ng g @angular-architects/ddd:domain booking
30-
ng g @angular-architects/ddd:domain boarding
31-
ng g @angular-architects/ddd:feature search --domain booking --app flight-app --entity flight
32-
ng g @angular-architects/ddd:feature cancel --domain booking --app flight-app
33-
ng g @angular-architects/ddd:feature manage --domain boarding --app flight-app
30+
ng g @angular-architects/ddd:domain booking --addApp
31+
ng g @angular-architects/ddd:domain boarding --addApp
32+
ng g @angular-architects/ddd:feature search --domain booking --entity flight
33+
ng g @angular-architects/ddd:feature cancel --domain booking
34+
ng g @angular-architects/ddd:feature manage --domain boarding
3435
```
3536

36-
Add domains and features interactively:
37+
For NGRX support, just add the ``--ngrx`` switch:
3738

3839
```
39-
ng g @angular-architects/ddd:domain
40-
> ? What is the name of the domain? booking
41-
> ? Would you like to add an associated application? (y/N) No
42-
43-
ng g @angular-architects/ddd:domain
44-
> ? What is the name of the domain? boarding
45-
> ? Would you like to add an associated application? (y/N) No
46-
47-
ng g @angular-architects/ddd:feature
48-
> ? What is the name of the library? search
49-
> ? What is the name of the associated domain? booking
50-
> ? Would you like to add the "feature-" prefix? (Y/n) Yes
51-
> ? Is this feature lazy loaded? (y/N) No
52-
> [Optional] What is the associated application? (Leave blank if none) flight-app
53-
> [Optional] What is the name of the entity to create for this feature? (Leave blank if none) flight
54-
55-
ng g @angular-architects/ddd:feature
56-
> ? What is the name of the library? cancel
57-
> ? What is the name of the associated domain? booking
58-
> ? Would you like to add the "feature-" prefix? (Y/n) Yes
59-
> ? Is this feature lazy loaded? (y/N) No
60-
> [Optional] What is the associated application? (Leave blank if none) flight-app
61-
> [Optional] What is the name of the entity to create for this feature? (Leave blank if none)
62-
63-
ng g @angular-architects/ddd:feature
64-
> ? What is the name of the library? manage
65-
> ? What is the name of the associated domain? boarding
66-
> ? Would you like to add the "feature-" prefix? (Y/n) Yes
67-
> ? Is this feature lazy loaded? (y/N) No
68-
> [Optional] What is the associated application? (Leave blank if none) flight-app
69-
> [Optional] What is the name of the entity to create for this feature? (Leave blank if none)
40+
ng g @angular-architects/ddd:domain booking --addApp --ngrx
41+
ng g @angular-architects/ddd:feature search --domain booking --entity flight --ngrx
42+
[...]
7043
```
7144

45+
7246
This example assumes that you have an app ``flight-app`` in place.
7347

7448
These schematics also wire up the individual libs. To see the result, create a dependency graph:
@@ -121,5 +95,9 @@ see https://github.com/angular-architects/ddd-demo
12195
- [Nrwl's eBook about monorepos and best practices](https://go.nrwl.io/angular-enterprise-monorepo-patterns-new-book)
12296
- [Recording of session about this architecture](https://www.youtube.com/watch?v=94HFD391zkE&t=1s)
12397
- [Article series about DDD with Angular](https://www.softwarearchitekt.at/aktuelles/sustainable-angular-architectures-1/)
124-
- [Our eBook about this architecture](https://leanpub.com/enterprise-angular)
98+
- [Our eBook on Angular and architectures](https://leanpub.com/enterprise-angular)
12599
- [Thomas Burlison's article about facades in Angular](https://medium.com/@thomasburlesonIA/push-based-architectures-with-rxjs-81b327d7c32d)
100+
101+
## More
102+
- [Angular Architecture Workshop](https://www.angulararchitects.io/en/angular-workshops/advanced-angular-enterprise-architecture-incl-ivy/)
103+
- [Follow us on Twitter](https://twitter.com/ManfredSteyer)

libs/ddd/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@angular-architects/ddd",
3-
"version": "1.0.5",
3+
"version": "1.1.0",
44
"license": "MIT",
55
"author": "Manfred Steyer",
66
"description": "Nx plugin for structuring a monorepo with domains and layers",
Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,41 @@
1-
import {
2-
chain,
3-
externalSchematic,
4-
Rule,
5-
apply,
6-
url,
7-
template,
8-
move,
1+
import {
2+
chain,
3+
externalSchematic,
4+
Rule,
5+
apply,
6+
url,
7+
template,
8+
move,
99
mergeWith,
1010
noop,
1111
} from '@angular-devkit/schematics';
1212

1313
import { strings } from '@angular-devkit/core';
1414
import { DomainOptions } from './schema';
15-
import { addDomainToLintingRules } from '../utils/update-linting-rules';
15+
import {
16+
addDomainToLintingRules,
17+
addNgrxImportsToApp,
18+
addNgRxToPackageJson,
19+
} from '../rules';
1620

17-
export default function(options: DomainOptions): Rule {
21+
export default function (options: DomainOptions): Rule {
1822
const libFolder = strings.dasherize(options.name);
1923

2024
const templateSource = apply(url('./files'), [
2125
template({}),
22-
move(`libs/${libFolder}/domain/src/lib`)
26+
move(`libs/${libFolder}/domain/src/lib`),
2327
]);
2428

29+
const appFolderName = strings.dasherize(options.name);
30+
const appPath = `apps/${appFolderName}/src/app`;
31+
const appModulePath = `${appPath}/app.module.ts`;
32+
33+
if (options.ngrx && !options.addApp) {
34+
throw new Error(
35+
`The 'ngrx' option may only be used when the 'addApp' option is used.`
36+
)
37+
}
38+
2539
return chain([
2640
externalSchematic('@nrwl/angular', 'lib', {
2741
name: 'domain',
@@ -34,12 +48,25 @@ export default function(options: DomainOptions): Rule {
3448
}),
3549
addDomainToLintingRules(options.name),
3650
mergeWith(templateSource),
37-
(!options.addApp) ?
38-
noop() :
39-
externalSchematic('@nrwl/angular', 'app', {
40-
name: options.name,
41-
tags: `domain:${options.name},type:app`,
42-
style: 'scss',
43-
}),
51+
!options.addApp
52+
? noop()
53+
: externalSchematic('@nrwl/angular', 'app', {
54+
name: options.name,
55+
tags: `domain:${options.name},type:app`,
56+
style: 'scss',
57+
}),
58+
options.addApp && options.ngrx
59+
? chain([
60+
externalSchematic('@ngrx/schematics', 'store', {
61+
project: options.name,
62+
root: true,
63+
minimal: true,
64+
module: 'app.module.ts',
65+
name: 'state',
66+
}),
67+
addNgrxImportsToApp(appModulePath),
68+
addNgRxToPackageJson(),
69+
])
70+
: noop(),
4471
]);
4572
}

libs/ddd/src/schematics/domain/schema.json

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,14 @@
1818
"x-prompt": "Would you like to add an associated application?",
1919
"default": false
2020
},
21+
"ngrx": {
22+
"type": "boolean",
23+
"default": false,
24+
"description": "Add ngrx for the associated app (addApp required)"
25+
},
2126
"type": {
2227
"type": "string",
23-
"enum": [
24-
"internal",
25-
"buildable",
26-
"publishable"
27-
],
28+
"enum": ["internal", "buildable", "publishable"],
2829
"description": "A type to determine if and how to build the library.",
2930
"default": "buildable"
3031
}

libs/ddd/src/schematics/domain/schema.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ export interface DomainOptions {
1414
* Add an app for the domain?
1515
*/
1616
addApp?: boolean;
17+
/**
18+
* Add ngrx for the associated app (addApp required)
19+
*/
20+
ngrx?: boolean;
1721
/**
1822
* A type to determine if and how to build the library.
1923
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { createAction, props } from '@ngrx/store';
2+
import { <%= classify(entity) %> } from '../../entities/<%= dasherize(entity) %>';
3+
4+
export const load<%= classify(entity) %> = createAction(
5+
'[<%= classify(entity) %>] Load <%= classify(entity) %>'
6+
);
7+
8+
export const load<%= classify(entity) %>Success = createAction(
9+
'[<%= classify(entity) %>] Load <%= classify(entity) %> Success',
10+
props<{ <%= camelize(entity) %>: <%= classify(entity) %>[] }>()
11+
);
12+
13+
export const load<%= classify(entity) %>Failure = createAction(
14+
'[<%= classify(entity) %>] Load <%= classify(entity) %> Failure',
15+
props<{ error: any }>()
16+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { Injectable } from '@angular/core';
2+
import { createEffect, Actions, ofType } from '@ngrx/effects';
3+
import { catchError, map, switchMap } from 'rxjs/operators';
4+
import { of } from 'rxjs';
5+
import * as <%= classify(entity) %>Actions from './<%= dasherize(entity) %>.actions';
6+
import { <%= classify(entity) %>DataService } from '../../infrastructure/<%= dasherize(entity) %>.data.service';
7+
8+
@Injectable()
9+
export class <%= classify(entity) %>Effects {
10+
load<%= classify(entity) %>$ = createEffect(() =>
11+
this.actions$.pipe(
12+
ofType(<%= classify(entity) %>Actions.load<%= classify(entity) %>),
13+
switchMap((action) =>
14+
this.<%=camelize(entity)%>DataService.load().pipe(
15+
map((<%= camelize(entity) %>) =>
16+
<%= classify(entity) %>Actions.load<%= classify(entity) %>Success({ <%= camelize(entity) %> })
17+
),
18+
catchError((error) =>
19+
of(<%= classify(entity) %>Actions.load<%= classify(entity) %>Failure({ error }))
20+
)
21+
)
22+
)
23+
)
24+
);
25+
26+
constructor(
27+
private actions$: Actions,
28+
private <%=camelize(entity)%>DataService: <%= classify(entity) %>DataService
29+
) { }
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { createReducer, on, Action } from '@ngrx/store';
2+
import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity';
3+
4+
import * as <%= classify(entity) %>Actions from './<%= dasherize(entity) %>.actions';
5+
import { <%= classify(entity) %> } from '../../entities/<%= dasherize(entity) %>';
6+
7+
export const <%= classify(entity).toUpperCase() %>_FEATURE_KEY = '<%= camelize(entity) %>';
8+
9+
export interface State extends EntityState<<%= classify(entity) %>> {
10+
selectedId ?: string | number; // which <%= classify(entity) %> record has been selected
11+
loaded : boolean; // has the <%= classify(entity) %> list been loaded
12+
error ?: string | null; // last known error (if any)
13+
}
14+
15+
export interface <%= classify(entity) %>PartialState {
16+
readonly [<%= classify(entity).toUpperCase() %>_FEATURE_KEY]: State;
17+
}
18+
19+
export const <%= camelize(entity) %>Adapter: EntityAdapter<<%= classify(entity) %>> = createEntityAdapter<<%= classify(entity) %>>();
20+
21+
export const initialState: State = <%= camelize(entity) %>Adapter.getInitialState({
22+
// set initial required properties
23+
loaded : false
24+
});
25+
26+
const <%= camelize(entity) %>Reducer = createReducer(
27+
initialState,
28+
on(<%= classify(entity) %>Actions.load<%= classify(entity) %>,
29+
state => ({ ...state, loaded: false, error: null })
30+
),
31+
on(<%= classify(entity) %>Actions.load<%= classify(entity) %>Success,
32+
(state, { <%= camelize(entity) %> }) => <%= camelize(entity) %>Adapter.upsertMany(<%= camelize(entity) %>, { ...state, loaded: true })
33+
),
34+
on(<%= classify(entity) %>Actions.load<%= classify(entity) %>Failure,
35+
(state, { error }) => ({ ...state, error })
36+
),
37+
);
38+
39+
export function reducer(state: State | undefined, action: Action) {
40+
return <%= camelize(entity) %>Reducer(state, action);
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { createFeatureSelector, createSelector } from '@ngrx/store';
2+
import { <%= classify(entity).toUpperCase() %>_FEATURE_KEY, State, <%= classify(entity) %>PartialState, <%= camelize(entity) %>Adapter } from './<%= dasherize(entity) %>.reducer';
3+
4+
// Lookup the '<%= classify(entity) %>' feature state managed by NgRx
5+
export const get<%= classify(entity) %>State = createFeatureSelector<<%= classify(entity) %>PartialState, State>(<%= camelize(entity).toUpperCase() %>_FEATURE_KEY);
6+
7+
const { selectAll, selectEntities } = <%= camelize(entity) %>Adapter.getSelectors();
8+
9+
export const get<%= classify(entity) %>Loaded = createSelector(
10+
get<%= classify(entity) %>State,
11+
(state: State) => state.loaded
12+
);
13+
14+
export const get<%= classify(entity) %>Error = createSelector(
15+
get<%= classify(entity) %>State,
16+
(state: State) => state.error
17+
);
18+
19+
export const getAll<%= classify(entity) %> = createSelector(
20+
get<%= classify(entity) %>State,
21+
(state: State) => selectAll(state)
22+
);
23+
24+
export const get<%= classify(entity) %>Entities = createSelector(
25+
get<%= classify(entity) %>State,
26+
(state: State) => selectEntities(state)
27+
);
28+
29+
export const getSelectedId = createSelector(
30+
get<%= classify(entity) %>State,
31+
(state: State) => state.selectedId
32+
);
33+
34+
export const getSelected = createSelector(
35+
get<%= classify(entity) %>Entities,
36+
getSelectedId,
37+
(entities, selectedId) => selectedId && entities[selectedId]
38+
);
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { Injectable } from '@angular/core';
2+
3+
import { select, Store, Action } from '@ngrx/store';
4+
5+
import * as from<%= classify(entity) %> from '../+state/<%= dasherize(entity) %>/<%= dasherize(entity) %>.reducer';
6+
import * as <%= classify(entity) %>Selectors from '../+state/<%= dasherize(entity) %>/<%= dasherize(entity) %>.selectors';
7+
8+
@Injectable({ providedIn: 'root' })
9+
export class <%= classify(name) %>Facade {
10+
loaded$ = this.store.pipe(select(<%= classify(entity) %>Selectors.get<%= classify(entity) %>Loaded));
11+
<%= camelize(entity) %>List$ = this.store.pipe(select(<%= classify(entity) %>Selectors.getAll<%= classify(entity) %>));
12+
selected<%= classify(entity) %>$ = this.store.pipe(select(<%= classify(entity) %>Selectors.getSelected));
13+
14+
constructor(private store: Store<from<%= classify(entity) %>.<%= classify(entity) %>PartialState>) { }
15+
16+
dispatch(action: Action) {
17+
this.store.dispatch(action);
18+
}
19+
}

0 commit comments

Comments
 (0)