I am writing my first application using DDD (in Node with TS) and I started writing all the domain first -- before starting the repositories/DB and then the application, while writing unit tests for each entity. As my domain developed, I started to have more doubts about my business logic. I'll give examples below.
One of my business logic states that a "requester" can create and delete tags. So, becuase I am writing all the domain first, I created a method createSectorTag
inside my Requester
entity. The method is defined below as follows:
public createSectorTag(data: ISectorTagData): number {
this.checkIfIsActive()
const sector_tag = new SectorTag(data)
return this.sector_tags.push(sector_tag)
}
As you can see, the method creates a SectorTag
entity and adds it to a private array in my Requester
entity. Since there's a bit of logic in this method, the this.checkIfIsActive()
, I think it makes sense for this piece of code exist inside my domain. But, at the same time, I think if it isn't too much work for a simple thing: I mean, every time my application calls my domain for a requester to add a tag, it'll have to create a Requester
entity, and then add it to requester.sector_tags; and after, I'll have to get the new SectorTag
and persist it to the DB with a repository.
Another example is the deleteSectorTag
method. This action has a logic that should validate if any of the request inside a requester have that tag, and if any has, an exception should be raised. The method's definition:
public deleteSectorTag(index: number): void {
/**
* Here I'll have to check if any of the requests inside the Request entity have
* the tag specified by the index in the parameter, and if so, raise an exception
*/
this.sector_tags.splice(index, 1)
}
But again, I believe that all this business logic adds a bit too much weight on processing. I'll have to get all the requests of a requester from the DB, create a Requester
entity, add the requests inside the entity and then make the validation. But it all seems like it could be a query in the DB for the validation.
Well, I really like the idea of putting all logic inside the domain; it makes me happy because it seems like my code gets closer and closer to reallity. It adapts to the way I think, but at the same time I am worried about performance.
If anyone can give me hints on this, I'd be very thankful.
Here is the full Requester
entity:
import { BudgetRequest, SectorTag, BudgetEstimator } from '@/domain/BudgetRequest/entities'
import { RequesterIsInactiveError } from '@/domain/BudgetRequest/exceptions'
import {
IBudgetRequestData,
IRequesterData,
ISectorTagData,
} from '@/domain/BudgetRequest/interfaces'
export class Requester {
private readonly active: boolean
private readonly name: string
private readonly cnpj: number
private budget_requests: Array<BudgetRequest> = []
private sector_tags: Array<SectorTag> = []
private budget_estimators: Array<BudgetEstimator> = []
constructor(data: IRequesterData) {
this.active = data.active
this.name = data.name
this.cnpj = data.cnpj
}
get budgets_list(): Array<BudgetRequest> {
return [...this.budget_requests]
}
get sector_tags_list(): Array<SectorTag> {
return [...this.sector_tags]
}
public createBudgetRequest(data: IBudgetRequestData): number {
this.checkIfIsActive()
const budget_request: BudgetRequest = new BudgetRequest(data)
return this.budget_requests.push(budget_request)
}
public createSectorTag(data: ISectorTagData): number {
this.checkIfIsActive()
const sector_tag = new SectorTag(data)
return this.sector_tags.push(sector_tag)
}
public deleteSectorTag(index: number): void {
/**
* Here
*/
this.sector_tags.splice(index, 1)
}
public bindSectorTagToBudgetRequest(i: number, j: number): void {
this.budget_requests[i].addSectorTag(this.sector_tags[j])
}
private checkIfIsActive(): void {
if (!this.active) {
throw new RequesterIsInactiveError()
}
}
}