stemmlerjs / ddd-forum

Hacker news-inspired forum app built with TypeScript using DDD practices from solidbook.io.
https://dddforum.com
ISC License
1.97k stars 454 forks source link

Why is lodash being imported into Comment Entity? #24

Open sagiomc opened 4 years ago

sagiomc commented 4 years ago

First of all thank you for share your knowledge with the community. I'm new to concepts like clean architecture and DDD so this repo and your blog has helped me a LOT for understanding. 🎉

The last two weeks I've been reading Uncle Bob - "Clean Architecture" and has been hard to me get the concept of an Entity(Enterprise Business Rules). According to the book(p. 190):

An Entity is an object within our computer system that embodies a small set of critical business rules operating on Critical Business Data.

I understand that they contain rules that are not application specific(Use Cases) and should not be affected by any external change, basically pure objects or data structures. So I thought that is broken when lodash is imported directly in the Comment entity.

Also, I don't know if it's the same case in class UniqueEntityID when importing the library uuid/v4 and then importing it in base entity Entity.

Would this make entities dependent on the lodash and uuid/v4 libraries?

stemmlerjs commented 3 years ago

@sagiomc Thanks for your comment!

...they contain rules that are not application-specific (Use Cases) and should not be affected by any external change

Remember the "Stable Dependency Principle"? In my eyes, lodash and uuid/v4 are stable dependencies because it is extremely unlikely that they will change. I would even argue that they have similar levels of stability as language features like JSON.stringify. That being said, I would still recommend choosing your dependencies carefully, but if you need to use a library to build your entity, and you can be sure that that library is small, stable, and unlikely to change, I think it's OK.

For example, if you needed to build an entity for a domain that involved really complex math and you knew that there was a library that was good at doing that math, you get to deduce whether that library is performant, serving the needs of the customer and stable. If not, you may have to build it yourself.

I believe this is a matter of principle vs practice/rules. Principles are more abstract and encompass more opportunities to help you decide how you should act in certain scenarios, while practices/rules are pretty rigid and don't leave too much room for adjustment.

Instead of the practice/rule of "entities are pure objects and data structures that don't have any dependencies", let's be reasonable; we could also write the uuid/v4 logic ourselves, but that would likely be wasted effort.

Here's how we're using lodash to check for the property of an object:

import { has } from 'lodash'
...
const defaultCommentProps: CommentProps = {
    ...props,
    points: has(props, 'points') ? props.points : 0,
    votes: props.votes ? props.votes : CommentVotes.create([])
  }

Looking at this again, it may be more performant to adjust the Lodash import to prevent loading all of it into memory:

import has from 'lodash/has'
...
const defaultCommentProps: CommentProps = {
    ...props,
    points: has(props, 'points') ? props.points : 0,
    votes: props.votes ? props.votes : CommentVotes.create([])
  }
sagiomc commented 3 years ago

Hi @stemmlerjs,

Thanks for your clear explanation. I completely agree with the tradeoffs of injecting some dependencies directly into entities, but I didn't know how to justify that. With the "Stable Dependency Principle" it's easier to make these kinds of decisions 😄

ydennisy commented 2 years ago

@stemmlerjs would it not be a good idea to remove the direct dependency, and allow the injection of it at runtime?