Domain-ization in Angular

Domain driven design (DDD) as a concept that “the structure and language of your code should match the business domain” is already known to benefit a vast group of software (and not only) products out in the market. However, there are also other secrets (hidden opportunities as many might say) behind this software design approach which many experienced professionals already know and effectively use on daily basis.
Angular is an application design framework and development platform for creating efficient and sophisticated single-page apps (SPAs). It is one of the de facto frameworks used, by a large number of companies, for releasing their products out in the market.
And here comes the marriage of Angular and DDD…
Domain Driven Design as a way of saying “Hey, what is your responsibility?”
Conceptually, a domain defines an area or a group of interest where a specific goal might be reached or something promised is provided. Angular already gives us something related that we can use:
- components
@Component({ … })
class ListComponent { … }
- injectables
@Injectable()
class ListService { … }
- modules
@NgModule({
providers: [ … ],
declarations: [ … ],
imports: [ … ],
exports: [ … ],
})
class ListModule { … }
Without repeating an already known knowledge about the benefits and more extensive classification (or different types) of Angular Elements like the ones above, there is an alternative way of thinking and deciding how to use them.
Components as View Controllers
Components could be used as controllers for the accompanied layout i.e template. That means that functions like the initializeForm below:
@Component({...})
class PaymentFormComponent {
form: FormGroup; ngOnInit() {
this.initializeForm();
} // this could easily be a very large implementation
private initializeForm() {
this.form = this.formBuilder.group({
name: ['', [Validatorsrequired]],
cardData: this.formBuilder.group({
cardType: ...
...
},
...
});
}
}
should NOT be inside the component.
The component might “want to setup a form model” but it is NOT its “responsibility” to do so. An injectable or a simple typescript file i.e payment-form.service.ts or payment-form.helpers.ts (name is up to you to decide) is more appropriate for this need.
Why???
- Because, you will minimize code written inside the component.
- Because, you will isolate domain specific code, which in this case is the “construction of your model (view, form e.t.c)” to a different place.
- Because you will modulate your code so that testing it becomes easier.
- Because you will extract code that can be reused.
- Because you will establish a clear ‘single responsibility’ pattern for the rest of the development team to easily locate code.
- Because, as it appears from previous statements, you will create easily maintained code which can be scaled with minimum cost.
Keep those in mind, next time you want to create ‘Clean, Maintainable, Scalable Code’ for the rest of developers to happily work on.
Injectables as Domain Providers
Utilizing Angular’s Dependency Injection (DI) system, injectables are a perfect way of isolating, encapsulating domains i.e features, minor or major specific functionality. Let’s take a look at an example:
- a CRUD service should do nothing more than satisfying create, read, update, delete for ‘todos’ domain:
@Injectable()
class TodosService { constructor(private http: HttpClient) {} getAll() {
return this.http.get(`${api}/todo`);
} getOne(id) {
return this.http.get(`${api}/todo/${id}`);
} createOne(payload) {
return this.http.post(`${api}/todo`, payload);
} updateOne(id, payload) {
return this.http.put(`${api}/todo/${id}`, payload);
} deletetOne(id) {
return this.http.delete(`${api}/todo/${id}`);
}}
Anything else, unrelated to “todos” and CRUD operations should NOT exist inside TodosService.
“If you say so, but…”
Why???
- Because you will be creating small, scoped and clean code. Look at the example above, it is only 25 lines of code!
- Because otherwise responsibility will be mixed with irrelevant functionality.
- Because extending CRUD can be easily done i.e perhaps by adding a “deleteAll” operation.
- Because testing it will be written once and could be reused in other similar CRUD based domains i.e perhaps another similar domain like “tasks”.
- Because, as it it obvious by previous statements, DRY patterns can be applied. I don’t have to point out the benefits in development time and patience that come out of this.
Modules as Greater Feature Bundles
Angular modules can take several forms (see Angular’s docs), but you only have to remember one thing. And that is that ngModules are nothing more than groupings of multiple domains or you could name them “hyper-domains”. For example:
@NgModule({
imports: [...]
})
class SharedProvidersModule {
static forRoot(): ModuleWithProviders {
return {
providers: [ CommonService, EnvironmentService, ApiService, ...],
ngModule: SharedProvidersModule
}
}@NgModule({
declarations: [ DateFormatDirective, BaseComponent, ... ],
exports: [ DateFormatDirective, BaseComponent, ... ],
})
class CommonComponentsModule { … }
I could show more examples but I believe those are enough to explain one basic concept. And that is domain separation or grouping on a greater level and scale.
Take a look at SharedProvidersModule. It groups several domains into a single domain provider module.
What about CommonComponentsModule. It shouts: “Hey you could use date-formating capabilities or reuse a common object like BaseComponent if you want your components to be consistent with our guidelines and meet high standards.
“Well, yes but…”
Why???
- Because Angular modules with a specific and well-defined purpose are easy to use and help developers understand what they provide.
- Because Angular modules which have a clear purpose can be combined or composed if you prefer the term ;)
- Because Angular modules with specific and thus probably small domain logic can be extracted and be reused to other products.
Final Words
As an experienced FrontEnd developer having worked on several companies, I have seen a lot of problems that come out from un-clean and unreasonably complex code which is primarily the effect of bad decisions. Domains are a way to solve this problem, so …
Embrace Domain Logic!
and you will soon see a difference in the way you organize your thoughts reflecting the way you code.
— Nikos
… more to come…
Feel free to contact me reverence23@gmail.com