Drive server WIP is the new API to Drive based on NestJS and following Clean Architecture and DDD(Domain Driven Design).
-
Yarn
npm i -g yarn
- Create a
.npmrcfile from the.npmrc.templateexample provided in the repo. - Replace
TOKENwith your own Github Personal Access Token withread:packagespermission ONLY - Use
yarnto install project dependencies.
Run yarn start to start server in production mode.
Run yarn start:dev to start with nodemon and development environment.
With docker-compose:
docker-compose upYou can run unit tests with:
yarn run testRunning e2e test requires creating a database first (check .env.test file), and having a mariadb instance running.
yarn run test:e2eThis project is based on NestJS and implements DDD (Domain Driven Design). Our implementation has these layers:
- Use cases
- Persistence
- Domain
- Controllers
The project has these main folders
- Modules
- Externals
- Config
- Middlewares
- Lib
In this folder we have all "use cases" or "context" where the business logic and the controllers (to respect Nest architecture) are located:
- Module: files with
*.module.ts - Use Cases: files with
*.usecase.ts - Controllers: files with
*.controller.ts - Domain: files with
*.domain.ts - Repository: files with
*.repository.ts
As an example, a 'file' module would be src/modules/file:
- Controllers:
file.controller.tsEndpoints for exposing the business logic of the file module. - Use Cases:
file.usecase.tsContains all the use-cases that are related to the file domain: moving a file, removing a file, etc. - Domain:
file.domain.tsClass File, all attributes and business logic are here, but we do not include anything about persistence or use cases. - Repository:
file.repository.tsFile that contains the interface that defines the signature of the methods to persist data and all the concrete implementations of this persistence (MariaDB, Redis..) - Module:
file.module.tsNest.js module
Based on Nest.js Controllers. Contains endpoints for exposing the business logic of the module.
Domain is an agnostic "entity" with the properties and the business logic, including global functionality to this domain. The domain does not persist the information , just changes the entity according to the situation.
Example of the File domain
# file.domain.ts
export class File implements FileAttributes {
fileId: number;
deleted: boolean;
deletedAt: boolean;
bucket: string;
constructor({...}){
...
}
moveToTrash() {
this.deleted = true;
this.deletedAt = new Date();
}
moveTo(bucket) {
if(this.bucket === bucket) {
throw Error('you cannot move file to this directory')
}
this.bucket = bucket;
}
}
The use case is a function that includes a real functional use case from the business model. To identify use cases we can use: "As User, I want ... for ... Example: "As Web User, I want to move files to trash for delete them later"
How to code a use case?
- Use a repository if there is a need to get any information from the entity in the database, the repositories should always return the entity of a domain.
- Update any properties of a domain or call functions with a business-logic. Ex: File.moveToTrash()
- Persist changes using a repository.
Example of use case:
# file.usecase.ts
export class FileUsecase {
constructor(private fileRepository: FileRepository) {}
async moveToTrash(fileId) {
const file = await this.fileRepository.findOne({ fileId });
if(!file) {
throw new Error('file not found');
}
file.moveToTrash();
await this.fileRepository.update(file);
return file;
}
async moveTo(fileId, destination) {
const file = await this.fileRepository.findOne({ fileId });
if(!file) {
throw new Error('file not found');
}
file.moveTo(destination);
await this.fileRepository.update(file);
return file;
}
}
The repository is part of the persistence layer.
A repository commonly has a model and a CRUD to interact with the entities of any concrete persistence implementation. The repository always returns an entity domain or a collection of them, so a repository should have adapters to parse from the model to the entity domain and viceversa.
Information about the repository pattern could be found here.
This folder contains third-party dependencies and external services whose usage could be necessary but it is not business-related.
This structure is based on modules structure of Nest.js.
A module can be found here if:
- Is an external API gateway.
- Is a bunch of logic that provides some 'service', as a cryptography service, a notifications service and any other logic that could be commonly found on generic folders like
utils,helpers, etc. But grouped by a context. For instance:encryptText()-> CryptoServicesendAnalytics()-> AnalyticsServiceparseStringToDate()-> StringService
This folder contains config files for the app.
This folder includes middlewares of any kind. You can find documentation about middlewares in Nest.js.
In this folder include only libraries to server HTTP and Nest.js
We use Swagger, which can be found up and running at /api endpoint via HTTP. You can find documentation of how to use it with Nest.js here.
This project is based on GNU License. You can show it in the License file.