Skip to content

internxt/drive-server-wip

Repository files navigation

Drive Server WIP

node Lines of Code Maintainability Rating Security Rating Reliability Rating Vulnerabilities Code Smells Duplicated Lines (%) Technical Debt Coverage Bugs

Drive server WIP is the new API to Drive based on NestJS and following Clean Architecture and DDD(Domain Driven Design).

Table of Contents

Quick Setup

  • Yarn

    npm i -g yarn

How to Install

  • Create a .npmrc file from the .npmrc.template example provided in the repo.
  • Replace TOKEN with your own Github Personal Access Token with read:packages permission ONLY
  • Use yarn to install project dependencies.

Start app

Run yarn start to start server in production mode.

Run yarn start:dev to start with nodemon and development environment.

Running in docker:

With docker-compose:

docker-compose up

Testing

Unit testing

You can run unit tests with:

yarn run test

End to End Testing

Running e2e test requires creating a database first (check .env.test file), and having a mariadb instance running.

yarn run test:e2e

Guideline Nest.js

This 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

Modules

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.ts Endpoints for exposing the business logic of the file module.
  • Use Cases: file.usecase.ts Contains all the use-cases that are related to the file domain: moving a file, removing a file, etc.
  • Domain: file.domain.ts Class File, all attributes and business logic are here, but we do not include anything about persistence or use cases.
  • Repository: file.repository.ts File 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.ts Nest.js module

Defining Controllers

Based on Nest.js Controllers. Contains endpoints for exposing the business logic of the module.

Defining Domain

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;
  }
}

Defining Usecases

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?

  1. 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.
  2. Update any properties of a domain or call functions with a business-logic. Ex: File.moveToTrash()
  3. 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;
  }
}

Defining Repository

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.

Externals

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() -> CryptoService
    • sendAnalytics() -> AnalyticsService
    • parseStringToDate() -> StringService

config

This folder contains config files for the app.

middlewares

This folder includes middlewares of any kind. You can find documentation about middlewares in Nest.js.

lib

In this folder include only libraries to server HTTP and Nest.js

API documentation

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.

License

This project is based on GNU License. You can show it in the License file.