-
Notifications
You must be signed in to change notification settings - Fork 647
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
typescript version #25
Comments
I don't have the time right now, but you're welcome to fork it and try and then I can add a link to your repo in my README. |
Hey @arcdev1, I'll try to make a TypeScript version of this project. I have just one question... Do you have any suggestion to where put the TS |
Would indeed be interesting to get a take on this. |
According to Bob Martin, the Interface should be defined by its consumer. He goes on at length about this and many other wonderful little gems in this blog post. So, yes, the use-case module should contain the data access Interface definition. |
Hello @arcdev1, first of all thank you for sharing this repo and your clear explanation in YouTube video 🙏 . All your advices had a huge impact in my organization to build better projects. I've been working on making a Typescript version for 2 months now. I did an incomplete first version, but I had the code within my company's control version system, so I can't share that repo and I couldn't find the time to share this with the community. Finally, I got the time and I would like to share my repo to contribute with the community and continue improve my skills with clean architectures using strongly typed languages by some feedback that you can share with me and others devs. 😄 The tests are incomplete, I only develop the test for domain / use cases so I have to work on the rest. |
Hi @sagiomc ... I'm just seeing this now. I'm going to spend a bit of time with it in the coming days so I can give you thorough feedback, but I wanted to take a moment to thank you for doing this and to acknowledge all the hard work you put in. At first glance, there's lots of good stuff here. Thank you! |
Hi @sagiomc, I've finally had some time to go over the Typescript codebase in more depth. I want to reiterate how much I appreciate the effort you put into this and how much good stuff is in there. I think there are some small tweaks that would make it even better. But I realized, re-reading your original post, you did not specifically ask for feedback. So, before I start doling out advice, I thought I would ask; are you okay with me replying here with some suggestions to improve the code? |
Hi @arcdev1, Thanks for taking the time and reviewing the codebase. I have no problem if you answer here, I'll appreciate all the advices to improve the code :) |
Thanks, @sagiomc. Here are my suggestions: 1. Don't allow Value Types to be instantiated with invalid dataYour code uses value types like Consider the code listing for export class UniqueEntityID extends Identifier<string | number> {
public constructor(id?: string | number) {
super(id ? id : uuidv4());
}
public isValidId(): boolean {
return TextUtils.isValidUuid(this.toString());
}
} Nothing stops us from writing: let id = new UniqueEntityID(" "); This means everywhere we use an instance of In general, a Type should serve as a contract that all instances conform to. So, I would rewrite the export class UniqueEntityID extends Identifier<string | number> {
public static of(id: string) {
UniqueEntityID.validate(id)
return new UniqueEntityID(id);
}
public static next() {
return new UniqueEntityID(uuidv4());
}
protected constructor(id?: string | number) {
super(id);
}
protected static validate(id: string) {
if(!UniqueEntityID.isValid(id)) {
throw new TypeError(`Invalid id.`);
}
}
public static isValid(id: string): boolean {
return TextUtils.isValidUuid(id);
}
} This way, it's impossible to create an invalid instance of
As a side note, importing the And finally, as a super picky, additional side note, "UniqueEntityID" is a very long name for something that's likely to get a lot of use. How about 2. Omit the word "get" from readonly property names.In general, it's good to have function names start with verbs, and property names start with nouns. But getters are a bit odd; they let us define a function that gets invoked through property access. So, for getters, I recommend using property naming conventions over function naming conventions. As a concrete example, in the Comment class, I would rename // current code
public get getAuthor(): string {
return this.author;
}
// suggested code
public get author(): string {
return this.author;
} This allows consumers to write code like: let comment = Comment.create(...);
let author = comment.author This feels more natural to me. The reason I used 3. Take advantage of
|
Thank you very much for your suggestions @arcdev1, I'll work on refactors following your advices in the coming weeks. Also, I have a couple of questions with value types and getters and setters: 1. Value TypesI wasn't aware of the potential bugs and duplicate code with the current implementation of the value types until your explanation, thanks for that, now it's very clear to me. Now, thinking about changing 2. Getter's and Setter'sI'm completely agree with the naming convention here. The only reason I don't use //property
private _author: string;
public get author(): string {
return this._author;
} I think this is because JavaScript lacks runtime encapsulation, an underscore usually signifies internal/private/encapsulated/property/variable. In my experience dangling underscores may be the most polarizing in JavaScript, so I decided avoid use dangling underscore for that reason... the single underscore prefix has become popular as a way to indicate "private" members of objects so perhaps I should start use this pattern too 😅 |
Hi @sagiomc, to answer your questions... I wouldn't bother creating a new directory for value types. In DDD we have a distinction between entities, aggregates, and value types, but in Clean Architecture, they're all just entities. As for use cases, they are generally considered Application Services in a DDD context if that makes sense. Regarding Getters and Setters, TypeScript now has support for the proposed ECMAScript private Class Fields. So you can actually achieve runtime encapsulation today. If you prefix a class field with a hash (instead of an underscore) it will be truly private. Some code to show you what I mean: class Privacy {
private _tsPrivate = "I'm only private in TypeScript at design time";
#reallyPrivate = "I'm private in TypeScript AND in JavaScript at design time AND runtime";
}
let p = new Privacy()
p._tsPrivate // no errors at runtime
p.reallyPrivate // will throw an error at runtime You can read more about JavaScript private Class Fields on the 2ality blog at https://2ality.com/2019/07/private-class-fields.html Hope that's helpful. Let me know if you have any other questions and good luck! |
It would be nice if you can make typescript version of this, thank you!
The text was updated successfully, but these errors were encountered: