UltraRangers / gauntlet

Gauntlet is a Typescript server template to kick-off your development fast 🔥🔥🔥
https://gauntlet-demo.herokuapp.com
MIT License
3 stars 0 forks source link

File structure #20

Closed ghost closed 6 years ago

ghost commented 6 years ago

I am thinking something like this. sample-structure Sorry cannot provide the actual folder structure in code. This is just an overview. The outside box is our application module (folder). Then basically inside are core, common and featured modules. But then we are building an application with a database so we are integrating a database module (folder). Inside database folder are all the database related files(entities, migrations, seeds and repositories) something like what I submit before.

jmaicaaan commented 6 years ago

Feature A would consists of what files? User (feature) inside it what are the files will be?

ghost commented 6 years ago

For my definition of a feature module (a module that provides the actual needs) then it should have a module, controller, a test, probably a service that provides function for the controller. Maybe it is good if a feature module is not tightly coupled to other modules?

ghost commented 6 years ago

That structure is the same for the client side. A core module for provision of services A common modules for the feature module (like alert modal, confim modal etc) A feature module that provides the actual needs

jmaicaaan commented 6 years ago

I get your definitions but I'm not yet clear on what would be inside the a feature folder. If it only be the controller and tests and an optional service?

ghost commented 6 years ago

Feature module should have a service always. Since no feature module that is so simple like sending a text message. All reusable logic will be on it service. It is a service then it can provide logic for other modules. But it is very rare that other modules will need a service for other modules. If that is the case then maybe that service or function should not be a feature service but a core application service since it does provide a service for 2 or more featured modules

ghost commented 6 years ago

The build for docker is fine, but it can't get started error says gauntlet-development database does not exists.

jmaicaaan commented 6 years ago

I actually had that structure also a few days ago. But I decided to put it all into one folder about that feature. So it will be the same folder structure to all. Unlike with the proposal to have a separate folder for entities and repositories like a database folder then inside it would have a folder for entities and repositories. Which would be another level for the folder unlike having them in one place all together. That is something I was thinking back then.

It is some how an anti-pattern because you have an entity folder for example that contains all the entities (eg: user.entity, role.entity) as separated from its feature. Grouping all related (current) vs a separated by type with a combination of related grouping (proposal). Did I miss something?

With the docker issue, let me address it later on. 🙆‍♂️

ghost commented 6 years ago

But the current was not actually grouped by relation since it has entity folder. If we are proceeding to related groupings then the user entity should be in user feature folder

ghost commented 6 years ago

I think the current is also a combination of feature groupings and type groupings.

jmaicaaan commented 6 years ago

Yeah currently it is not there. But it can be easily move there. What do you think about it?

ghost commented 6 years ago

Yeah we can move it on the feature folder, but still we are having common folder which is a group of types. Before we can identify which pattern is an anti pattern we should agree on what pattern we will use. There are two categories

In a type isolation the app will have entities, pipes, controller, tests, repositories, services and etc. The files are structured based on types

In a feature isolation the app will only have folders that are features, like user folder then all related to user is inside that folder. Like entities servics repository

So as we can see these categories we know, for me we should not be limited and stick on these two, these categories are just guidelines. If we are going to choose and stick into that then the easiest is the type isolation. Very straightforward and simple. But scaling application will be hard since there files that are dependent of each other, while the feature isolation solves that, putting all related in a single folder but there are some files that are independent like migrations and seeds

So the basic solution we wrote is to have the combination of the two. I believe current is a combination and that is good solution for me. Since if one applcation is big and set to scale then there is not only one pattern inside the app. Basically our soultion made was identify the indepedent files and have them separated. Then identify the related and have them put together. And that is a combination, resulting into another pattern.

Generally the combination is what I am looking for since the beginning, it just how do we implement it?

ghost commented 6 years ago

If we are to choose, I will go for the combination. My reason is that it is nice to have files that are global (services) and files that build the actual needs (features)

jmaicaaan commented 6 years ago

I think the combination is also good. If we were to continue with the feature type, which is almost currently, then the common folder like what you said would look like the a structure that is group by types. Which I believe is fine because it is a common folder and not a feature that is limited/self-dedicated to specific controller.

Summarizing from what I understand: The server folder would contain a core, common and feature folders which could be a number of (x) folders

In the core would be about the application itself, the whole application. (bootstraping, upping of server). Do we have structure also here?

In the common would be all the common files for the app, could be use by the core or feature folders. This is group by types (services, pipes, etc..)

In a feature folder, would consist of all the related files in a specific feature like the user.repositories, user.entity, user.getUsersController, user.postUserController, user.service. The controllers inside this would be a lot if not maintained properly since we are doing single controller per api endpoint.

ghost commented 6 years ago

In client side the core module consists of services and only imported once in the app module, to provide service. And the common module consists if actual components that are reusable and will be use by feature modules. And will be instantiated if needed, so the common module are destroyed after it was use and can re instantiated more than once so this is not a singleton. But in server since modules are all singleton on creation of the app. Whether we import x times it will just send us the same instance of that module. So having that info we can delete the core folder here in server.

So the only thing I need to ask is that where to put database module. Specially the seeds and migrations and database service

jmaicaaan commented 6 years ago

I don't get much the idea of the client side. I can see it to reflect as what the server looks like inside the app folder in the client side.

For the core in the server, we will remove it and have a flat structure for the core folder which I think is not that bad since we don't have really that much of a "core" files 🤣

Actually looking at it, the seeds and migrations is not really part of the database module. It is not really part of it because we don't add it in ng @ngModule declaration. The only thing that we add there is the database providers and the database service. Can we put this provider and service into a common folder and have a util folder?

Correct me if I'm wrong, my definition of a util folder is a place where we put the files that can be executed in a non-server infrastructure like the a script thing. We could easily "run" these files without doing the npm start.

ghost commented 6 years ago

I agree, you are correct the migrations and seeds are not injected in db module, so we put it inside utils folder right? So that is not really part of a run time application? If so then I am thinking a structure like this Server folder:

Or we put the utils inside the app? Is the database module inside the app since it is part of the run time application?

jmaicaaan commented 6 years ago

This would be great since both client and server resembles perfectly. Which I believe is a positive thing since our server is inspired by angular. Better to have a one structure for both. So when you are in the server/client you still familiar with the structure and not get lost..

Yeah the database module should be inside the app since the feature modules needed the database module. And we needed the database on start application. Apparently, I don't know how can the utils folder get the database connection? Options I have: 1st option - Will remain there as illustrated above and have it import the database 2nd option - Remove the app folder (but it breaks the client-server structure 😢) and have it the same level directory. See current folders. 3rd option - Have a "dedicated" database for the utils. Though this it not needed and breaks the DRY principle.

Currently, if you could look at the loading of database module. I am not adding it in the app.module. I do add it in the feature module. I forgot what problem I've had on loading it back then. Anyways, we could fix it later on after this. Just to let you know about it

ghost commented 6 years ago

I am not sure why we need db connection in utils. Inside utils are seeds and migrations, right? If we are to run migrations using typeorm then it will get the ormconfig we have the the typeorm will create the connection automatically, while in seeds if we are going to run this independently we probably need to create a connection manually based on ormconfig. But I wonder why we need to run seeds manually, I think this is just for testing purposes

If the we are sure that the data will be part of th esystem then it should be on migration like roles. So in production, only the migration should be run.

jmaicaaan commented 6 years ago

I believe we already had a conversation in regards to the seed. Where we have a seed that will be changable somehow what happened before in myfit. Do you still remember? Hahaha

ghost commented 6 years ago

Yeah, we can isolate it here for now. But if we need to run seeds indepedently we need to create our own scripts to connect to db, like what typeorm did.

I Still have questions here in structure: -Entities and repositories will be inside the feature folder right? Since these are feature based and not a global one. So we will.map the entity from ormconfig and import it in repository then import repository in db module then db module will be imported in user module or feature module. Is the flow appropriate? Or expected? That the entity and repository having inside the feature then get imported in db module then imported back to user

jmaicaaan commented 6 years ago

I'm guessing that typeORM can use regex path. If yes we could do something /dist/server/app/**/*.entity.ts. Otherwise, we are doomed 👿 kidding

Otherwise that would be an extra load work to be filled in loading.

Making the process clear:

ghost commented 6 years ago

Yes, it just like we really need db module for the repository to work. So the only flow we have is to register it in db module.then import db module back to a feature module. Kind of like feature module is depending something in global that is running in background

jmaicaaan commented 6 years ago

Sort of, because if they needed a repository it really needs to depend on the database which is on the "background". Do you have any ideas on it?

ghost commented 6 years ago

Hmm I browse the repository pattern in nest. So we don't really need to import the provider in db module, we can import it in user module. The db module creates only the connection if imported, then finds all providers inside the module that db has been imported and attached the connection. Hmm ok if this works I agree that we can put entity repository in the feature module

The flow is that we have user module for example, then it has user entity, repository and provider. The provider will be a component in usermodule then db module will be imported. If this works the inside the app folder we have:

ghost commented 6 years ago

I just red this https://docs.nestjs.com/recipes/sql-typeorm documentation

jmaicaaan commented 6 years ago

I see, I thought we must add the provider in the database module. This is great since in our current one I already have the .provider.ts in a feature folder.

So there will be a database folder which contains the database module and the database provider. Only those files?

ghost commented 6 years ago

Hmm not sure if we will put it in a folder but if that is a module inside the app I will probably put it. For now not a case too much for me. Since the only concern I have is the flow. I am good with this, if I will have some feedback maybe not this major. Thank you JM, haha can't tag using cp.

jmaicaaan commented 6 years ago

I think we are near the final stage of this structuring. Let's clear it out again tomorrow then plan/start for the next steps. Haha thank you as well! 😄

ghost commented 6 years ago

Hi @jmaicaaan For the structure this is the final one server: -app: -feature modules -main.ts -setup.ts

In a feature module, we can see that we have a controller, repository and a service. We should have a standard rules on how to use that. For me repository is a class on where we can add functions that ensures data input and output in a db layer, more something like db constraints. This can be validation for database or I can say inner validation or database validation. So basically this has the most minimal functions and mostly related to insert function. service is a class where inject repository and implements reusable and complete logic for a feature module *controller is a class on where it validates user data from the client. This validation is somehow different from the validation in a db layer (for example I have a foreign key '1' from client but it doesn't exists) we will not validate this in the controller because on the first place controller doesn't have a direct contact in db only the repository. Also we should only import here is the service and not repository.

Summary; Repository handles db validation and all db layer stuff. Service implements reusable and complete logic for the controller or module, can have data validation but not using pipe (import repository here) Controller gets the data parse data, pipe data validation (import service here).

jmaicaaan commented 6 years ago

Hmm, there are some instances where we need directly the repository method and adding unnecessary abstraction just cost us

For example, in my user.repository.ts I have a method, getAllUsers() which returns a list of users. Just as simple as that (purpose of example/use case only). Then before I could use that I needed to add in my user.service.ts another method that does nothing except that it calls and returns the userRepository.getAllUsers()

What do you think on that use case?

ghost commented 6 years ago

That is reasonable too. But for me there is only small portion of the app that is so simple like that. Probably in case of users I will also need to populate the roles of each user by default in every return of getAllUsers(). That is why I will put this in a service. For the best use case is that if you will get a list from independent tables like roles (no relations) then it is reasonable to import repository.

ghost commented 6 years ago

In the case of big models I can't see that we need to import repository directly to the service. It may happen only in cases of small independent/reference tables. Like Roles, probably can have no service at all too.

jmaicaaan commented 6 years ago

I believe why we keep on changing and updating these things is because we want to make sure the consumer of this repository would have a clean code and maintainable one.

I just thought that whatever we do, it is still their responsibility to create a clean code no matter how the app is structured. Whether it can be a single file per endpoint controller or a single file that contains every endpoints BUT make use of services/utils (delegating tasks properly. Having the single responsibility principle).

I also would like to have the final update on this branch that would contain the single file controller per entity like you did in the another branch. But I would disagree on having the repository just be imported in the service. I believe they can be both injected as dependency to the controller for easier usage. It comes down again on how the consumer can create a clean code. It is their freedom to utilize both.

Comment back if you can make the necessary changes, if not I'll be the one to make it up tomorrow. Then I'll merge this one into the master as our new baseline.

jmaicaaan commented 6 years ago

Updated master as the new baseline. Further development must be branch off from it