loopbackio / loopback-next

LoopBack makes it easy to build modern API applications that require complex integrations.
https://loopback.io
Other
4.92k stars 1.06k forks source link

Support repository mixins when scaffolding repositories and rest-crud APIs #5565

Open bajtos opened 4 years ago

bajtos commented 4 years ago

Suggestion

At the moment, we allow sharing of repository code via Repository Base Classes only. The command lb4 repository looks for src/repositories/*.repository.base.ts and treat them as repository base classes to offer in the prompt:

$ lb4 repository
? Please select the datasource DbDatasource
? Select the model(s) you want to generate a repository for Product
? Please select the repository base class (Use arrow keys)
  DefaultCrudRepository (Legacy juggler bridge)
  ----- Custom Repositories -----
❯ AuditingRepository

While easy to use, Repository Base Classes have few shortcomings too.

  1. JavaScript does not support multiple inheritance, thus it's not possible to combine behavior from multiple repository base classes in the same model-specific repository class.

  2. Inheritance-based reuse is considered as an anti-pattern in Object Oriented Design, it's recommended to use composition instead ("prefer composition over inheritance").

Let's enhance lb4 repository and lb4 rest-crud commands to all allow the user to choose which Repository Mixins to apply.

Examples

Model-specific repository class:

$ lb4 repository
? Please select the datasource DbDatasource
? Select the model(s) you want to generate a repository for Product
? Please select the repository base class DefaultCrudRepository (Legacy juggler bridge)
? Select the mixin(s) you want to apply (Press <space> to select, <a> to toggle all, <i> to invert 
selection)
❯◉ AuditingMixin (src/mixins/auditing.repository-mixin.ts)
 ◯ TimeStampMixin (src/mixins/timestamp.repository-mixin.ts)

Emitted code:

// src/repositories/product.repository.ts
import {Constructor, inject} from '@loopback/core';
import {DefaultCrudRepository} from '@loopback/repository';
import {DbDataSource} from '../datasources';
import {AuditingRepositoryMixin} from '../mixins/auditing.repository-mixin';
import {Product, ProductRelations} from '../models';

export class ProductRepository extends AuditingRepositoryMixin<
  Product,
  Constructor<
    DefaultCrudRepository<
      Product,
      typeof Product.prototype.id,
      ProductRelations
    >
  >
>(DefaultCrudRepository) {
  constructor(@inject('datasources.db') dataSource: DbDataSource) {
    super(Product, dataSource);
  }
}

Model API booter (rest-crud)

$ lb4 rest-crud
? Please select the datasource DbDatasource
? Select the model(s) you want to generate a CRUD REST endpoint Category
? Select the mixin(s) you want to apply (Press <space> to select, <a> to toggle all, <i> to invert 
selection)
❯◉ AuditingMixin (src/mixins/auditing.repository-mixin.ts)
 ◯ TimeStampMixin (src/mixins/timestamp.repository-mixin.ts)

Emitted code:

// src/model-endpoints/category.rest-config.ts
import {ModelCrudRestApiConfig} from '@loopback/rest-crud';
import {AuditingRepositoryMixin} from '../mixins/auditing.repository-mixin';
import {Category} from '../models';

const config: ModelCrudRestApiConfig = {
  model: Category,
  pattern: 'CrudRest',
  dataSource: 'db',
  basePath: '/categories',
  repositoryMixins: [
    AuditingRepositoryMixin
  ],
};
module.exports = config;

(This will require us to enhance Model API booters to support the new config option repositoryMixins.)

Acceptance criteria

jimmy1wu commented 4 years ago

hello @bajtos, i am interested in working on this feature. can i take this issue/epic? 😊

dougal83 commented 4 years ago

Hi @jimmy1wu, I've assigned you this epic. Thank you!