How to manage modules in NestJS?
How to manage modules in NestJS?
by Maciej Sikorski




App
App
Users
App
Users
Notifications
App
Users
Notifications
App
Users
Notifications
Cats
App
Users
Notifications
Cats
App
Users
Notifications
Cats
Dogs
Permissions


@Module({
imports: [
forwardRef(() => CatsModule),
forwardRef(() => UsersModule),
],
providers: [NotificationService],
exports: [NotificationService],
})
export class NotificationModule {}







@Module({
imports: [
forwardRef(() => CatsModule),
forwardRef(() => UsersModule),
],
providers: [NotificationService],
exports: [NotificationService],
})
export class NotificationModule {}




What will you learn today?
- Why do we fail at defining modules
- How to find good module's boundaries
- What is the purpose of a module
Chapter I
How do we define modules?
import { Injectable } from '@nestjs/common';
import { Cat } from './interfaces/cat.interface'
@Injectable()
class CatsService {
private cats: Cat[] = [];
create(cat: Cat) {
this.cats.push(cat);
}
findAll(): Cat[] {
return this.cats;
}
}
import { Injectable } from '@nestjs/common';
import { Cat } from './interfaces/cat.interface'
@Injectable()
class CatsService {
private cats: Cat[] = [];
create(cat: Cat) {
//...
}
update(cat: Cat) {
//...
}
remove(catId: number) {
//...
}
findAll(): Cat[] {
//...
}
findOne(catId: number): Cat {
//...
}
}
import { Injectable } from '@nestjs/common';
import { Cat } from './interfaces/cat.interface'
@Injectable()
class CatsService {
private cats: Cat[] = [];
create(cat: Cat) {
//...
}
update(cat: Cat) {
//...
}
remove(catId: number) {
//...
}
findAll(): Cat[] {
//...
}
findOne(catId: number): Cat {
//...
}
}

import { Injectable } from '@nestjs/common';
import { Cat } from './interfaces/cat.interface'
@Injectable()
class CatsService {
//...
adopt(catId: number, clientId: number) {
}
returnCat(catId: number) {
}
}
import { Injectable } from '@nestjs/common';
import { Cat } from './interfaces/cat.interface'
@Injectable()
class CatsService {
//...
adopt(catId: number, clientId: number) {
}
returnCat(catId: number) {
}
feed(catId: number) {
}
clean(catId: number) {
}
physicalExamination(catId: number, data: ExaminationData) {
}
changePlace(catId: number, placeId: number) {
}
}
import { Injectable } from '@nestjs/common';
import { Cat } from './interfaces/cat.interface'
@Injectable()
class CatsService {
adopt(catId: number, clientId: number) {
}
returnCat(catId: number) {
}
}
import { Injectable } from '@nestjs/common';
import { Cat } from './interfaces/cat.interface'
@Injectable()
class CatsService {
constructor(private clientService: ClientService) {}
adopt(catId: number, clientId: number) {
if(!this.clientService.canAdopt(clientId)) {
throw new Error(`Client ${clientId} can't adopt a cat`);
}
}
returnCat(catId: number) {
}
}
import { Injectable } from '@nestjs/common';
import { Cat } from './interfaces/cat.interface'
@Injectable()
class CatsService {
constructor(private clientService: ClientService) {}
adopt(catId: number, clientId: number) {
if(!this.clientService.canAdopt(clientId)) {
throw new Error(`Client ${clientId} can't adopt a cat`);
}
// The rest of process
}
returnCat(catId: number) {
}
}
import { Injectable } from '@nestjs/common';
import { Cat } from './interfaces/cat.interface'
@Injectable()
class CatsService {
constructor(private clientService: ClientService) {}
adopt(catId: number, clientId: number) {
if(!this.clientService.canAdopt(clientId)) {
throw new Error(`Client ${clientId} can't adopt a cat`);
}
// The rest of process
}
returnCat(catId: number) {
const cat = this.catsRepository.findOne(cat);
this.clientService.banNextAdoption(cat.ownerId);
// The rest of the process
}
}
CatsModule
class CatsService {
adopt(clientId, catId) {}
returnCat(catId) {}
}
CatsModule
class CatsService {
adopt(clientId, catId) {}
returnCat(catId) {}
}
ClientsModule
class ClientService {
canAdopt(clientId) {}
banNextAdoption(clientId) {}
}
CatsModule
class CatsService {
adopt(clientId, catId) {}
returnCat(catId) {}
}
ClientsModule
class ClientService {
canAdopt(clientId) {}
banNextAdoption(clientId) {}
}
CatsModule
class CatsService {
adopt(clientId, catId) {}
returnCat(catId) {}
}
ClientsModule
class ClientService {
canAdopt(clientId) {}
banNextAdoption(clientId) {}
sponsorFeed(clientId, catId, food) {}
}
CatsModule
class CatsService {
adopt(clientId, catId) {}
returnCat(catId) {}
}
ClientsModule
class ClientService {
canAdopt(clientId) {}
banNextAdoption(clientId) {}
sponsorFeed(clientId, catId, food) {
// ...
this.increaseAdoptionProbability(clientId);
// ...
}
increaseAdoptionProbability(clientId) {}
}
CatsModule
class CatsService {
adopt(clientId, catId) {}
returnCat(catId) {}
}
ClientsModule
class ClientService {
canAdopt(clientId) {}
banNextAdoption(clientId) {}
sponsorFeed(clientId, catId, food) {
// ...
this.increaseAdoptionProbability(clientId);
this.catsService.increaseCatBudget(catId, amount);
// ...
}
increaseAdoptionProbability(clientId) {}
}
CatsModule
class CatsService {
adopt(clientId, catId) {}
returnCat(catId) {}
}
ClientsModule
class ClientService {
constructor(private catsService: CatsService) {}
canAdopt(clientId) {}
banNextAdoption(clientId) {}
sponsorFeed(clientId, catId, food) {
// ...
this.increaseAdoptionProbability(clientId);
this.catsService.increaseCatBudget(catId, amount);
// ...
}
increaseAdoptionProbability(clientId) {}
}
ClientsModule
class ClientService {
constructor(private catsService: CatsService) {}
canAdopt(clientId) {}
banNextAdoption(clientId) {}
sponsorFeed(clientId, catId, food) {
// ...
this.increaseAdoptionProbability(clientId);
this.catsService.increaseCatBudget(catId, amount);
// ...
}
increaseAdoptionProbability(clientId) {}
}
CatsModule
class CatsService {
adopt(clientId, catId) {}
returnCat(catId) {}
increaseCatBudget(catId, amount) {}
}
ClientsModule
class ClientService {
constructor(private catsService: CatsService) {}
canAdopt(clientId) {}
banNextAdoption(clientId) {}
sponsorFeed(clientId, catId, food) {
// ...
this.increaseAdoptionProbability(clientId);
this.catsService.increaseCatBudget(catId, amount);
// ...
}
increaseAdoptionProbability(clientId) {}
}
CatsModule
class CatsService {
adopt(clientId, catId) {}
returnCat(catId) {}
increaseCatBudget(catId, amount) {}
}
ClientsModule
class ClientService {
constructor(private catsService: CatsService) {}
canAdopt(clientId) {}
banNextAdoption(clientId) {}
sponsorFeed(clientId, catId, food) {
// ...
this.increaseAdoptionProbability(clientId);
this.catsService.increaseCatBudget(catId, amount);
// ...
}
increaseAdoptionProbability(clientId) {}
}
CatsModule
class CatsService {
adopt(clientId, catId) {}
returnCat(catId) {}
increaseCatBudget(catId, amount) {}
}


What we do wrong?
What we do wrong?

- Logical cohesion
What we do wrong?

- Logical cohesion
- Functional cohesion
What we do wrong?

- Logical cohesion
- Focusing on nouns
- Functional cohesion
What we do wrong?

- Logical cohesion
- Focusing on nouns
- Functional cohesion
- Focusing on verbs
What we do wrong?

- Logical cohesion
- Focusing on nouns
- Building modules around entities & data structure
- Functional cohesion
- Focusing on verbs
What we do wrong?

- Logical cohesion
- Focusing on nouns
- Building modules around entities & data structure
- Functional cohesion
- Focusing on verbs
- Building modules & entities around a problem & processes
Chapter II
Let's apply it to our app
ClientsModule
class ClientService {
constructor(private catsService: CatsService) {}
canAdopt(clientId) {}
banNextAdoption(clientId) {}
sponsorFeed(clientId, catId, food) {
// ...
this.increaseAdoptionProbability(clientId);
this.catsService.increaseCatBudget(catId, amount);
// ...
}
increaseAdoptionProbability(clientId) {}
}
CatsModule
class CatsService {
adopt(clientId, catId) {}
returnCat(catId) {}
increaseCatBudget(catId, amount) {}
}
ClientsModule
class ClientService {
canAdopt(clientId) {}
banNextAdoption(clientId) {}
sponsorFeed(clientId, catId, food) {}
increaseAdoptionProbability(clientId) {}
}
CatsModule
class CatsService {
adopt(clientId, catId) {}
returnCat(catId) {}
increaseCatBudget(catId, amount) {}
}
ClientsModule
class ClientService {
canAdopt(clientId) {}
banNextAdoption(clientId) {}
sponsorFeed(clientId, catId, food) {}
increaseAdoptionProbability(clientId) {}
}
CatsModule
class CatsService {
adopt(clientId, catId) {}
returnCat(catId) {}
increaseCatBudget(catId, amount) {}
}
AdoptionModule
class AdoptionService {
}
ClientsModule
class ClientService {
canAdopt(clientId) {}
banNextAdoption(clientId) {}
sponsorFeed(clientId, catId, food) {}
increaseAdoptionProbability(clientId) {}
}
CatsModule
class CatsService {
increaseCatBudget(catId, amount) {}
}
AdoptionModule
class AdoptionService {
adopt(clientId, catId) {}
returnCat(catId) {}
}
ClientsModule
class ClientService {
canAdopt(clientId) {}
banNextAdoption(clientId) {}
sponsorFeed(clientId, catId, food) {}
increaseAdoptionProbability(clientId) {}
}
CatsModule
class CatsService {
increaseCatBudget(catId, amount) {}
}
AdoptionModule
class AdoptionService {
adopt(clientId, catId) {}
returnCat(catId) {}
}
ClientsModule
class ClientService {
canAdopt(clientId) {}
banNextAdoption(clientId) {}
sponsorFeed(clientId, catId, food) {}
increaseAdoptionProbability(clientId) {}
}
CatsModule
class CatsService {
increaseCatBudget(catId, amount) {}
}
AdoptionModule
class AdoptionService {
adopt(clientId, catId) {}
returnCat(catId) {}
}

ClientsModule
class ClientService {
canAdopt(clientId) {}
banNextAdoption(clientId) {}
sponsorFeed(clientId, catId, food) {}
increaseAdoptionProbability(clientId) {}
}
CatsModule
class CatsService {
increaseCatBudget(catId, amount) {}
}
AdoptionModule
class AdoptionService {
adopt(clientId, catId) {}
returnCat(catId) {}
}
VerificationModule
class VerificationService {
}
ClientsModule
class ClientService {
sponsorFeed(clientId, catId, food) {}
increaseAdoptionProbability(clientId) {}
}
CatsModule
class CatsService {
increaseCatBudget(catId, amount) {}
}
AdoptionModule
class AdoptionService {
adopt(clientId, catId) {}
returnCat(catId) {}
}
VerificationModule
class VerificationService {
canAdopt(clientId) {}
banNextAdoption(clientId) {}
}
ClientsModule
class ClientService {
sponsorFeed(clientId, catId, food) {}
increaseAdoptionProbability(clientId) {}
}
CatsModule
class CatsService {
increaseCatBudget(catId, amount) {}
}
AdoptionModule
class AdoptionService {
adopt(clientId, catId) {}
returnCat(catId) {}
}
VerificationModule
class VerificationService {
canAdopt(clientId) {}
banNextAdoption(clientId) {}
}
ClientsModule
class ClientService {
sponsorFeed(clientId, catId, food) {}
}
CatsModule
class CatsService {
increaseCatBudget(catId, amount) {}
}
AdoptionModule
class AdoptionService {
adopt(clientId, catId) {}
returnCat(catId) {}
}
VerificationModule
class VerificationService {
canAdopt(clientId) {}
banNextAdoption(clientId) {}
increaseAdoptionProbability(clientId) {}
}
ClientsModule
class ClientService {
sponsorFeed(clientId, catId, food) {}
}
CatsModule
class CatsService {
increaseCatBudget(catId, amount) {}
}
AdoptionModule
class AdoptionService {
adopt(clientId, catId) {}
returnCat(catId) {}
}
VerificationModule
class VerificationService {
canAdopt(clientId) {}
banNextAdoption(clientId) {}
increaseAdoptionProbability(clientId) {}
}
ClientsModule
class ClientService {
sponsorFeed(clientId, catId, food) {}
}
CatsModule
class CatsService {
increaseCatBudget(catId, amount) {}
}
AdoptionModule
class AdoptionService {
adopt(clientId, catId) {}
returnCat(catId) {}
}
VerificationModule
class VerificationService {
canAdopt(clientId) {}
banNextAdoption(clientId) {}
increaseAdoptionProbability(clientId) {}
}
ClientsModule
class ClientService {
sponsorFeed(clientId, catId, food) {}
}
CatsModule
class CatsService {
increaseCatBudget(catId, amount) {}
}
AdoptionModule
class AdoptionService {
adopt(clientId, catId) {}
returnCat(catId) {}
}
VerificationModule
class VerificationService {
canAdopt(clientId) {}
banNextAdoption(clientId) {}
increaseAdoptionProbability(clientId) {}
}
CharityWorkModule
class CharityWorkService {
}
ClientsModule
class ClientService {
}
CatsModule
class CatsService {
increaseCatBudget(catId, amount) {}
}
AdoptionModule
class AdoptionService {
adopt(clientId, catId) {}
returnCat(catId) {}
}
VerificationModule
class VerificationService {
canAdopt(clientId) {}
banNextAdoption(clientId) {}
increaseAdoptionProbability(clientId) {}
}
CharityWorkModule
class CharityWorkService {
sponsorFeed(clientId, catId, food) {}
}
ClientsModule
class ClientService {
}
CatsModule
class CatsService {
increaseCatBudget(catId, amount) {}
}
AdoptionModule
class AdoptionService {
adopt(clientId, catId) {}
returnCat(catId) {}
}
VerificationModule
class VerificationService {
canAdopt(clientId) {}
banNextAdoption(clientId) {}
increaseAdoptionProbability(clientId) {}
}
CharityWorkModule
class CharityWorkService {
sponsorFeed(clientId, catId, food) {}
}
BudgetModule
class BudgetService {
}
ClientsModule
class ClientService {
}
CatsModule
class CatsService {
}
AdoptionModule
class AdoptionService {
adopt(clientId, catId) {}
returnCat(catId) {}
}
VerificationModule
class VerificationService {
canAdopt(clientId) {}
banNextAdoption(clientId) {}
increaseAdoptionProbability(clientId) {}
}
CharityWorkModule
class CharityWorkService {
sponsorFeed(clientId, catId, food) {}
}
BudgetModule
class BudgetService {
increaseCatBudget(catId, amount) {}
}
ClientsModule
class ClientService {
}
CatsModule
class CatsService {
create(cat) {}
update(cat) {}
delete(catId) {}
read(catId) {}
}
AdoptionModule
class AdoptionService {
adopt(clientId, catId) {}
returnCat(catId) {}
}
VerificationModule
class VerificationService {
canAdopt(clientId) {}
banNextAdoption(clientId) {}
increaseAdoptionProbability(clientId) {}
}
CharityWorkModule
class CharityWorkService {
sponsorFeed(clientId, catId, food) {}
}
BudgetModule
class BudgetService {
increaseCatBudget(catId, amount) {}
}
ClientsModule
class ClientService {
create(client) {}
update(client) {}
delete(clientId) {}
read(clientId) {}
}
CatsModule
class CatsService {
create(cat) {}
update(cat) {}
delete(catId) {}
read(catId) {}
}
AdoptionModule
class AdoptionService {
adopt(clientId, catId) {}
returnCat(catId) {}
}
VerificationModule
class VerificationService {
canAdopt(clientId) {}
banNextAdoption(clientId) {}
increaseAdoptionProbability(clientId) {}
}
CharityWorkModule
class CharityWorkService {
sponsorFeed(clientId, catId, food) {}
}
BudgetModule
class BudgetService {
increaseCatBudget(catId, amount) {}
}
Chapter III
Too bad it's not that simple

ClientsModule
class ClientService {
create(client) {}
update(client) {}
delete(clientId) {}
read(clientId) {}
}
CatsModule
class CatsService {
create(cat) {}
update(cat) {}
delete(catId) {}
read(catId) {}
}
AdoptionModule
class AdoptionService {
adopt(clientId, catId) {}
returnCat(catId) {}
}
VerificationModule
class VerificationService {
canAdopt(clientId) {}
banNextAdoption(clientId) {}
increaseAdoptionProbability(clientId) {}
}
CharityWorkModule
class CharityWorkService {
sponsorFeed(clientId, catId, food) {}
}
BudgetModule
class BudgetService {
increaseCatBudget(catId, amount) {}
}
ClientsModule
class ClientService {
create(client) {}
update(client) {}
delete(clientId) {}
read(clientId) {}
}
CatsModule
class CatsService {
create(cat) {}
update(cat) {}
delete(catId) {}
read(catId) {}
}
AdoptionModule
class AdoptionService {
adopt(clientId, catId) {}
returnCat(catId) {}
}
VerificationModule
class VerificationService {
canAdopt(clientId) {}
banNextAdoption(clientId) {}
increaseAdoptionProbability(clientId) {}
}
CharityWorkModule
class CharityWorkService {
sponsorFeed(clientId, catId, food) {}
}
BudgetModule
class BudgetService {
increaseCatBudget(catId, amount) {}
}
ClientsModule
class ClientService {
create(client) {}
update(client) {}
delete(clientId) {}
read(clientId) {}
}
CatsModule
class CatsService {
create(cat) {}
update(cat) {}
delete(catId) {}
read(catId) {}
}
AdoptionModule
class AdoptionService {
adopt(clientId, catId) {}
returnCat(catId) {}
}
VerificationModule
class VerificationService {
canAdopt(clientId) {}
banNextAdoption(clientId) {}
increaseAdoptionProbability(clientId) {}
}
CharityWorkModule
class CharityWorkService {
sponsorFeed(clientId, catId, food) {}
}
BudgetModule
class BudgetService {
increaseCatBudget(catId, amount) {}
}
ClientsModule
class ClientService {
create(client) {}
update(client) {}
delete(clientId) {}
read(clientId) {}
}
CatsModule
class CatsService {
create(cat) {}
update(cat) {}
delete(catId) {}
read(catId) {}
}
AdoptionModule
class AdoptionService {
adopt(clientId, catId) {}
returnCat(catId) {}
}
VerificationModule
class VerificationService {
canAdopt(clientId) {}
banNextAdoption(clientId) {}
increaseAdoptionProbability(clientId) {}
}
CharityWorkModule
class CharityWorkService {
sponsorFeed(clientId, catId, food) {}
}
BudgetModule
class BudgetService {
increaseCatBudget(catId, amount) {}
}
ClientsModule
class ClientService {
create(client) {}
update(client) {}
delete(clientId) {}
read(clientId) {}
}
CatsModule
class CatsService {
create(cat) {}
update(cat) {}
delete(catId) {}
read(catId) {}
}
AdoptionModule
class AdoptionService {
adopt(clientId, catId) {}
returnCat(catId) {}
}
ClientsModule
class ClientService {
create(client) {}
update(client) {}
delete(clientId) {}
read(clientId) {}
}
CatsModule
class CatsService {
create(cat) {}
update(cat) {}
delete(catId) {}
read(catId) {}
}
AdoptionModule
class AdoptionService {
adopt(clientId, catId) {}
returnCat(catId) {}
}
ClientsModule
class ClientService {
create(client) {}
update(client) {}
delete(clientId) {}
read(clientId) {}
}
CatsModule
class CatsService {
create(cat) {}
update(cat) {}
delete(catId) {}
read(catId) {}
}
AdoptionModule
class AdoptionService {
adopt(clientId, catId) {}
returnCat(catId) {}
}
CatPreview
class CatPreview {
preview(catId) {}
}
CatsModule
class CatsService {
create(cat) {}
update(cat) {}
delete(catId) {}
read(catId) {}
}
AdoptionModule
class AdoptionService {
adopt(clientId, catId) {}
returnCat(catId) {}
}
BudgetModule
class BudgetService {
increaseCatBudget(catId, amount) {}
}
CatsModule
class CatsService {
create(cat) {}
update(cat) {}
delete(catId) {}
read(catId) {}
}
AdoptionModule
class AdoptionService {
adopt(clientId, catId) {}
returnCat(catId) {}
}
AddCatModule
class AddCatService {
add(catData) {}
}
BudgetModule
class BudgetService {
increaseCatBudget(catId, amount) {}
}
Integration patterns
- fetching/calling another module synchronously
- data layer integration
- async events exchange
- etc.
Chapter IV
Plot twist
ClientsModule
class ClientService {
create(client) {}
update(client) {}
delete(clientId) {}
read(clientId) {}
}
CatsModule
class CatsService {
create(cat) {}
update(cat) {}
delete(catId) {}
read(catId) {}
}
AdoptionModule
class AdoptionService {
adopt(clientId, catId) {}
returnCat(catId) {}
}
VerificationModule
class VerificationService {
canAdopt(clientId) {}
banNextAdoption(clientId) {}
increaseAdoptionProbability(clientId) {}
}
CharityWorkModule
class CharityWorkService {
sponsorFeed(clientId, catId, food) {}
}
BudgetModule
class BudgetService {
increaseCatBudget(catId, amount) {}
}
ClientsModule
class ClientService {
create(client) {}
update(client) {}
delete(clientId) {}
read(clientId) {}
}
CatsModule
class CatsService {
create(cat) {}
update(cat) {}
delete(catId) {}
read(catId) {}
}
AdoptionModule
class AdoptionService {
adopt(clientId, catId) {}
returnCat(catId) {}
}
VerificationModule
class VerificationService {
canAdopt(clientId) {}
banNextAdoption(clientId) {}
increaseAdoptionProbability(clientId) {}
}
CharityWorkModule
class CharityWorkService {
sponsorFeed(clientId, catId, food) {}
}
BudgetModule
class BudgetService {
increaseCatBudget(catId, amount) {}
}
class ClientService {
create(client) {}
update(client) {}
delete(clientId) {}
read(clientId) {}
}
AppModule
class CatsService {
create(cat) {}
update(cat) {}
delete(catId) {}
read(catId) {}
}
class AdoptionService {
adopt(clientId, catId) {}
returnCat(catId) {}
}
class VerificationService {
canAdopt(clientId) {}
banNextAdoption(clientId) {}
increaseAdoptionProbability(clientId) {}
}
class CharityWorkService {
sponsorFeed(clientId, catId, food) {}
}
class BudgetService {
increaseCatBudget(catId, amount) {}
}
class Clients {
create(client) {}
update(client) {}
delete(clientId) {}
read(clientId) {}
}
class Cats {
create(cat) {}
update(cat) {}
delete(catId) {}
read(catId) {}
}
class Adoption {
start(clientId, catId) {}
returnCat(catId) {}
}
class Verification {
canAdopt(clientId) {}
banNextAdoption(clientId) {}
increaseAdoptionProbability(clientId) {}
}
class CharityWork {
sponsorFeed(clientId, catId, food) {}
}
class Budget {
increase(budgetId, amount) {}
}
Why do we even need modules?
Why do we even need modules?
- To specify the public interface and hide the internals
- To configure what is a part of an application
- For testing, to use only what necessary or modify the setup
Next time - Pragmatic NestJS
Thank you!
maciej-sikorski-a01b26149

@_MaciejSikorski


Sikora00

nestjs-talks.com
How to manage modules in NestJS?
By Maciej Sikorski
How to manage modules in NestJS?
- 177