Skip to content

Commit ffcd084

Browse files
authored
Merge pull request #113 from dolittle/projections-di
Simplify Dependency Injection of Projections using IProjectionOf<TReadModel>
2 parents 4469a2c + b4a3d93 commit ffcd084

24 files changed

+403
-47
lines changed

Samples/ExpressJS/index.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import createApplication, { static as serveStatic } from 'express';
77
import { json } from 'body-parser';
88
import { Logger } from 'winston';
99
import { dolittle, inject } from '@dolittle/sdk.extensions.express';
10-
import { IProjectionStore } from '@dolittle/sdk.projections';
10+
import { IProjectionOf } from '@dolittle/sdk.projections';
1111

1212
import { DishCounter } from './DishCounter';
1313
import { DishPrepared } from './DishPrepared';
@@ -28,10 +28,10 @@ application.post('/prepare', (req, res, next) => {
2828

2929
application.get(
3030
'/counters',
31-
inject(IProjectionStore, 'Logger')(
32-
(req, res, next, projections, logger: Logger) => {
33-
logger.info('Received reqest to get DishCounter projection');
34-
projections.getAll(DishCounter)
31+
inject(IProjectionOf.for(DishCounter), 'Logger')(
32+
(req, res, next, dishCounter, logger: Logger) => {
33+
logger.info('Received request to get DishCounter projection');
34+
dishCounter.getAll()
3535
.then(result => res.json(result))
3636
.catch(next);
3737
}

Samples/ExpressJS/public/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@
2626
.then(counters => {
2727
console.log('Fetched projection result', counters);
2828
table.replaceChildren();
29-
for (const { dish, numberOfTimesPrepared } of counters) {
29+
for (const { name, numberOfTimesPrepared } of counters) {
3030
const row = document.createElement('tr');
31-
row.appendChild(document.createElement('td')).innerText = dish;
31+
row.appendChild(document.createElement('td')).innerText = name;
3232
row.appendChild(document.createElement('td')).innerText = numberOfTimesPrepared;
3333
table.appendChild(row);
3434
}

Source/aggregates/Builders/AggregateRootsModelBuilder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export class AggregateRootsModelBuilder {
3535
aggregateRootTypes.associate(type, identifier.aggregateRootType);
3636

3737
this._bindings.addTenantServices(binder => {
38-
binder.bind(`IAggregateOf<${type.name}>`).toFactory(services => services.get(IAggregates).of(type));
38+
binder.bind(IAggregateOf.for(type)).toFactory(services => services.get(IAggregates).of(type));
3939
});
4040
}
4141
return aggregateRootTypes;

Source/aggregates/IAggregateOf.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
// Copyright (c) Dolittle. All rights reserved.
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

4+
import { Constructor } from '@dolittle/types';
5+
6+
import { Abstract, ServiceIdentifier } from '@dolittle/sdk.dependencyinversion';
47
import { EventSourceIdLike } from '@dolittle/sdk.events';
8+
59
import { AggregateRoot } from './AggregateRoot';
610
import { IAggregateRootOperations } from './IAggregateRootOperations';
711

@@ -10,7 +14,6 @@ import { IAggregateRootOperations } from './IAggregateRootOperations';
1014
* @template TAggregate
1115
*/
1216
export abstract class IAggregateOf<TAggregate extends AggregateRoot> {
13-
1417
/**
1518
* Create a new {@link AggregateRoot} with a random {@link EventSourceId}.
1619
* @returns {IAggregateRootOperations<TAggregate>}
@@ -23,4 +26,13 @@ export abstract class IAggregateOf<TAggregate extends AggregateRoot> {
2326
* @returns {IAggregateRootOperations<TAggregate>}
2427
*/
2528
abstract get(eventSourceId: EventSourceIdLike): IAggregateRootOperations<TAggregate>;
29+
30+
/**
31+
* Gets a {@link ServiceIdentifier} for an {@link AggregateRoot} type to inject an {@link IAggragateOf} from the service provider.
32+
* @param {Constructor} type - The type of the aggregate root.
33+
* @returns {Abstract} The service identifier to use for injection.
34+
*/
35+
static for<TAggregate extends AggregateRoot>(type: Constructor<TAggregate>): Abstract<IAggregateOf<TAggregate>> {
36+
return `IAggregateOf<${type.name}>` as any;
37+
}
2638
}

Source/artifacts/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ export {
77
ITypeMap,
88
TypeMap,
99
ComplexValueMap,
10+
TypeNotAssociatedWithKey
1011
} from './Mappings/_exports';

Source/dependencyInversion/TenantServiceProviders.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export class TenantServiceProviders extends ITenantServiceProviders {
6060
const tenantContainer = this._rootContainer.createChild();
6161

6262
tenantContainer.bind(TenantId).toConstantValue(tenant);
63-
tenantContainer.bind('TenantId').toConstantValue(tenant);
63+
tenantContainer.bind(TenantId.name).toConstantValue(tenant);
6464

6565
const tenantBinder = new InversifyServiceBinder(tenantContainer);
6666
bindings.bindAllTenantServices(tenantBinder, tenant);

Source/projections/Builders/ProjectionsModelBuilder.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

44
import { IClientBuildResults, IModel } from '@dolittle/sdk.common';
5+
import { IServiceProviderBuilder } from '@dolittle/sdk.dependencyinversion';
56
import { IEventTypes } from '@dolittle/sdk.events';
67

78
import { ProjectionProcessor } from '../Internal/ProjectionProcessor';
@@ -10,6 +11,8 @@ import { ProjectionReadModelTypes } from '../Store/ProjectionReadModelTypes';
1011
import { ProjectionBuilder } from './ProjectionBuilder';
1112
import { ProjectionClassBuilder } from './ProjectionClassBuilder';
1213
import { isProjectionModelId } from '../ProjectionModelId';
14+
import { IProjectionStore } from '../Store/IProjectionStore';
15+
import { IProjectionOf } from '../Store/IProjectionOf';
1316

1417
/**
1518
* Represents a builder that can build {@link ProjectionProcessor} from an {@link IModel}.
@@ -20,11 +23,13 @@ export class ProjectionsModelBuilder {
2023
* @param {IModel} _model - The built application model.
2124
* @param {IClientBuildResults} _buildResults - For keeping track of build results.
2225
* @param {IEventTypes} _eventTypes - For event types resolution.
26+
* @param {IServiceProviderBuilder} _bindings - For registering the bindings for {@link IProjectionOf} types.
2327
*/
2428
constructor(
2529
private readonly _model: IModel,
2630
private readonly _buildResults: IClientBuildResults,
2731
private readonly _eventTypes: IEventTypes,
32+
private readonly _bindings: IServiceProviderBuilder
2833
) {}
2934

3035
/**
@@ -46,8 +51,10 @@ export class ProjectionsModelBuilder {
4651
const readModelTypes = new ProjectionReadModelTypes();
4752
for (const { identifier, type } of identifiers) {
4853
readModelTypes.associate(type, identifier.id, identifier.scope);
54+
this._bindings.addTenantServices(binder => {
55+
binder.bind(IProjectionOf.for(type)).toFactory(services => services.get(IProjectionStore).of(type, identifier.id, identifier.scope));
56+
});
4957
}
50-
5158
return [processors, readModelTypes];
5259
}
5360
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright (c) Dolittle. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
import { Constructor } from '@dolittle/types';
5+
6+
import { Abstract, ServiceIdentifier } from '@dolittle/sdk.dependencyinversion';
7+
import { Cancellation } from '@dolittle/sdk.resilience';
8+
import { ScopeId } from '@dolittle/sdk.events';
9+
10+
import { Key } from '../Key';
11+
import { CurrentState } from './CurrentState';
12+
import { ProjectionId } from '../ProjectionId';
13+
14+
/**
15+
* Defines a system that knows about a projection.
16+
* @template TReadModel The type of the projection read model.
17+
*/
18+
export abstract class IProjectionOf<TReadModel> {
19+
/**
20+
* Gets the {@link ProjectionId} identifier.
21+
*/
22+
abstract readonly identifier: ProjectionId;
23+
24+
/**
25+
* Gets the {@link ScopeId}.
26+
*/
27+
abstract readonly scope: ScopeId;
28+
29+
/**
30+
* Gets the projection read model by key.
31+
* @param {Key | any} key - The key of the projection.
32+
* @param {Cancellation} [cancellation] - The optional cancellation token.
33+
* @returns {Promise<TReadModel>} A {@link Promise} that when resolved returns the read model of the projection.
34+
*/
35+
abstract get(key: Key | any, cancellation?: Cancellation): Promise<TReadModel>;
36+
37+
/**
38+
* Gets the projection state by key.
39+
* @param {Key | any} key - The key of the projection.
40+
* @param {Cancellation} [cancellation] - The optional cancellation token.
41+
* @returns {Promise<CurrentState<TReadModel>>} A {@link Promise} that when resolved returns the current state of the projection.
42+
*/
43+
abstract getState(key: Key | any, cancellation?: Cancellation): Promise<CurrentState<TReadModel>>;
44+
45+
/**
46+
* Gets all projection read models.
47+
* @param {Cancellation} [cancellation] - The optional cancellation token.
48+
* @returns {Promise<TReadModel>} A {@link Promise} that when resolved returns all the read models of the projection.
49+
*/
50+
abstract getAll(cancellation?: Cancellation): Promise<TReadModel[]>;
51+
52+
/**
53+
* Gets a {@link ServiceIdentifier} for a Projection read model type to inject an {@link IProjectionOf} from the service provider.
54+
* @param {Constructor} type - The type of the projection read model.
55+
* @returns {Abstract} The service identifier to use for injection.
56+
*/
57+
static for<TReadModel>(type: Constructor<TReadModel>): Abstract<IProjectionOf<TReadModel>> {
58+
return `IProjectionOf<${type.name}>` as any;
59+
}
60+
}

Source/projections/Store/IProjectionStore.ts

Lines changed: 52 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,38 @@ import { Cancellation } from '@dolittle/sdk.resilience';
1010
import { Key } from '../Key';
1111
import { ProjectionId } from '../ProjectionId';
1212
import { CurrentState } from './CurrentState';
13+
import { IProjectionOf } from './IProjectionOf';
1314

1415
/**
1516
* Defines the API surface for getting projections.
1617
*/
1718
export abstract class IProjectionStore {
19+
20+
/**
21+
* Gets the {@link IProjectionOf} the projection read model type.
22+
* @param {Constructor<TProjection>} type - The type of the projection.
23+
* @returns {IProjectionOf<TProjection>} The {@link IProjectionOf} for the {@link TProjection} projection read model type.
24+
* @template TProjection The type of the projection read model.
25+
*/
26+
abstract of<TProjection>(type: Constructor<TProjection>): IProjectionOf<TProjection>;
27+
/**
28+
* Gets the {@link IProjectionOf} the projection read model type.
29+
* @param {Constructor<TReadModel>} type - The type of the projection.
30+
* @param {ProjectionId | Guid | string} projection - The id of the projection.
31+
* @returns {IProjectionOf<TReadModel>} The {@link IProjectionOf} for the {@link TReadModel} projection read model type.
32+
* @template TReadModel The type of the projection read model.
33+
*/
34+
abstract of<TReadModel>(type: Constructor<TReadModel>, projection: ProjectionId | Guid | string): IProjectionOf<TReadModel>;
35+
/**
36+
* Gets the {@link IProjectionOf} the projection read model type.
37+
* @param {Constructor<TReadModel>} type - The type of the projection.
38+
* @param {ProjectionId | Guid | string} projection - The id of the projection.
39+
* @param {ScopeId | Guid | string} scope - The scope the projection in.
40+
* @returns {IProjectionOf<TReadModel>} The {@link IProjectionOf} for the {@link TReadModel} projection read model type.
41+
* @template TReadModel The type of the projection read model.
42+
*/
43+
abstract of<TReadModel>(type: Constructor<TReadModel>, projection: ProjectionId | Guid | string, scope: ScopeId | Guid | string): IProjectionOf<TReadModel>;
44+
1845
/**
1946
* Gets a projection read model by key for a projection associated with a type.
2047
* @param {Constructor<TProjection>} type - The type of the projection.
@@ -27,26 +54,26 @@ export abstract class IProjectionStore {
2754

2855
/**
2956
* Gets a projection read model by key for a projection specified by projection identifier.
30-
* @param {Constructor<TProjection>} type - The type of the projection.
57+
* @param {Constructor<TReadModel>} type - The type of the projection.
3158
* @param {Key | any} key - The key of the projection.
3259
* @param {ProjectionId | Guid | string} projection - The id of the projection.
3360
* @param {Cancellation} [cancellation] - The cancellation token.
34-
* @returns {Promise<TProjection>} A {@link Promise} that when resolved returns the read model of the projection.
35-
* @template TProjection The type of the projection.
61+
* @returns {Promise<TReadModel>} A {@link Promise} that when resolved returns the read model of the projection.
62+
* @template TReadModel The type of the projection.
3663
*/
37-
abstract get<TProjection>(type: Constructor<TProjection>, key: Key | any, projection: ProjectionId | Guid | string, cancellation?: Cancellation): Promise<TProjection>;
64+
abstract get<TReadModel>(type: Constructor<TReadModel>, key: Key | any, projection: ProjectionId | Guid | string, cancellation?: Cancellation): Promise<TReadModel>;
3865

3966
/**
4067
* Gets a projection read model by key for a projection specified by projection and scope identifier.
41-
* @param {Constructor<TProjection>} type - The type of the projection.
68+
* @param {Constructor<TReadModel>} type - The type of the projection.
4269
* @param {Key | any} key - The key of the projection.
4370
* @param {ProjectionId | Guid | string} projection - The id of the projection.
4471
* @param {ScopeId | Guid | string} scope - The scope the projection in.
4572
* @param {Cancellation} [cancellation] - The cancellation token.
46-
* @returns {Promise<TProjection>} A {@link Promise} that when resolved returns the read model of the projection.
47-
* @template TProjection The type of the projection.
73+
* @returns {Promise<TReadModel>} A {@link Promise} that when resolved returns the read model of the projection.
74+
* @template TReadModel The type of the projection.
4875
*/
49-
abstract get<TProjection>(type: Constructor<TProjection>, key: Key | any, projection: ProjectionId | Guid | string, scope: ScopeId | Guid | string, cancellation?: Cancellation): Promise<TProjection>;
76+
abstract get<TReadModel>(type: Constructor<TReadModel>, key: Key | any, projection: ProjectionId | Guid | string, scope: ScopeId | Guid | string, cancellation?: Cancellation): Promise<TReadModel>;
5077

5178
/**
5279
* Gets a projection read model by key for a projection specified by projection identifier.
@@ -61,7 +88,7 @@ export abstract class IProjectionStore {
6188
* Gets a projection read model by key for a projection specified by projection and scope identifier.
6289
* @param {Key | any} key - The key of the projection.
6390
* @param {ProjectionId | Guid | string} projection - The id of the projection.
64-
* @param {ScopeId | Guid | string} scpåe - The scope the projection in.
91+
* @param {ScopeId | Guid | string} scope - The scope the projection in.
6592
* @param {Cancellation} [cancellation] - The cancellation token.
6693
* @returns {Promise<any>} A {@link Promise} that when resolved returns the read model of the projection.
6794
*/
@@ -79,24 +106,24 @@ export abstract class IProjectionStore {
79106

80107
/**
81108
* Gets all projection read models for a projection specified by projection identifier.
82-
* @param {Constructor<TProjection>} type - The type of the projection.
109+
* @param {Constructor<TReadModel>} type - The type of the projection.
83110
* @param {ProjectionId | Guid | string} projection - The id of the projection.
84111
* @param {Cancellation} [cancellation] - The cancellation token.
85-
* @returns {Promise<TProjection[]>} A {@link Promise} that when resolved returns the all the read models of the projection.
86-
* @template TProjection The type of the projection.
112+
* @returns {Promise<TReadModel[]>} A {@link Promise} that when resolved returns the all the read models of the projection.
113+
* @template TReadModel The type of the projection.
87114
*/
88-
abstract getAll<TProjection>(type: Constructor<TProjection>, projection: ProjectionId | Guid | string, cancellation?: Cancellation): Promise<TProjection[]>;
115+
abstract getAll<TReadModel>(type: Constructor<TReadModel>, projection: ProjectionId | Guid | string, cancellation?: Cancellation): Promise<TReadModel[]>;
89116

90117
/**
91118
* Gets all projection read models for a projection specified by projection and scope identifier.
92-
* @param {Constructor<TProjection>} type - The type of the projection.
119+
* @param {Constructor<TReadModel>} type - The type of the projection.
93120
* @param {ProjectionId | Guid | string} projection - The id of the projection.
94121
* @param {ScopeId | Guid | string} scope - The scope the projection in.
95122
* @param {Cancellation} [cancellation] - The cancellation token.
96-
* @returns {Promise<TProjection[]>} A {@link Promise} that when resolved returns the all the read models of the projection.
97-
* @template TProjection The type of the projection.
123+
* @returns {Promise<TReadModel[]>} A {@link Promise} that when resolved returns the all the read models of the projection.
124+
* @template TReadModel The type of the projection.
98125
*/
99-
abstract getAll<TProjection>(type: Constructor<TProjection>, projection: ProjectionId | Guid | string, scope: ScopeId | Guid | string, cancellation?: Cancellation): Promise<TProjection[]>;
126+
abstract getAll<TReadModel>(type: Constructor<TReadModel>, projection: ProjectionId | Guid | string, scope: ScopeId | Guid | string, cancellation?: Cancellation): Promise<TReadModel[]>;
100127

101128
/**
102129
* Gets all projection read models for a projection specified by projection identifier.
@@ -127,26 +154,26 @@ export abstract class IProjectionStore {
127154

128155
/**
129156
* Gets a projection state by key for a projection specified by projection identifier.
130-
* @param {Constructor<TProjection>} type - The type of the projection.
157+
* @param {Constructor<TReadModel>} type - The type of the projection.
131158
* @param {Key | any} key - The key of the projection.
132159
* @param {ProjectionId | Guid | string} projection - The id of the projection.
133160
* @param {Cancellation} [cancellation] - The cancellation token.
134-
* @returns {Promise<CurrentState<TProjection>>} A {@link Promise} that when resolved returns the current state of the projection.
135-
* @template TProjection The type of the projection.
161+
* @returns {Promise<CurrentState<TReadModel>>} A {@link Promise} that when resolved returns the current state of the projection.
162+
* @template TReadModel The type of the projection.
136163
*/
137-
abstract getState<TProjection>(type: Constructor<TProjection>, key: Key | any, projection: ProjectionId | Guid | string, cancellation?: Cancellation): Promise<CurrentState<TProjection>>;
164+
abstract getState<TReadModel>(type: Constructor<TReadModel>, key: Key | any, projection: ProjectionId | Guid | string, cancellation?: Cancellation): Promise<CurrentState<TReadModel>>;
138165

139166
/**
140167
* Gets a projection state by key for a projection specified by projection and scope identifier.
141-
* @param {Constructor<TProjection>} type - The type of the projection.
168+
* @param {Constructor<TReadModel>} type - The type of the projection.
142169
* @param {Key | any} key - The key of the projection.
143170
* @param {ProjectionId | Guid | string} projection - The id of the projection.
144171
* @param {ScopeId | Guid | string} scope - The scope the projection in.
145172
* @param {Cancellation} [cancellation] - The cancellation token.
146-
* @returns {Promise<CurrentState<TProjection>>} A {@link Promise} that when resolved returns the current state of the projection.
147-
* @template TProjection The type of the projection.
173+
* @returns {Promise<CurrentState<TReadModel>>} A {@link Promise} that when resolved returns the current state of the projection.
174+
* @template TReadModel The type of the projection.
148175
*/
149-
abstract getState<TProjection>(type: Constructor<TProjection>, key: Key | any, projection: ProjectionId | Guid | string, scope: ScopeId | Guid | string, cancellation?: Cancellation): Promise<CurrentState<TProjection>>;
176+
abstract getState<TReadModel>(type: Constructor<TReadModel>, key: Key | any, projection: ProjectionId | Guid | string, scope: ScopeId | Guid | string, cancellation?: Cancellation): Promise<CurrentState<TReadModel>>;
150177

151178
/**
152179
* Gets a projection state by key for a projection specified by projection identifier.

0 commit comments

Comments
 (0)