Repositories
Repositories are classes or components that encapsulate the logic required to access data sources. They centralize common data access functionality, providing better maintainability and decoupling the infrastructure or technology used to access databases from the domain model layer. If you use an Object-Document Mapper (ODM) like Mongoose, the code required to query or manipulate the documents is simplified. This lets you focus on the data persistence logic rather than on data access plumbing.
For every document in our app, we'll have a respective repository against that document. Document shouldn't be fetched directly without Repository doing
this will be considered Anti Pattren and should be avoided. All use cases related to how the app will fetch manipulate and processa document
should reside inside the Repository of that respective document.
All basic database operations which we normally need to perform on a Document like the basic CRUD operations are already defined inside the BaseRepository class.
So you need to re-write the basic stuff and focus more on advance stuff. Each repository should extend the BaseRepository so it can
benefit from all these pre cooked methods.
The methods provided by BaseRepositiry are following
export interface IBaseRepository<T, D = Document<T>> {
    // Returns the total document count which fullfils the provided condition
    count(conditions?: FilterQuery<D>): Promise<number>;
    // Returns the list of documents which fullfils the provided condition (if any)
    // Provides out of box paginaytion using `limit` and `skip` params
    // sort attribute can be used to get sorted results
    // selected fields takes a comma serperated strinh of fields name to be returned.
    getAll(options: GetAllArgs<D>): Promise<T[]>;
    
    // Returns single document
    get(conditions?: FilterQuery<D>, selectedFields?: string): Promise<T>;
    // Creates new document
    create<I>(data: I): Promise<T>;
    // Create/Update (If already exists) new document
    upsert<I>(conditions: FilterQuery<D>, update: I, options: any): Promise<T>;
    // Update existing document
    update<I>(criteria: FilterQuery<D>, update: UpdateQuery<D>, options?: any): Promise<T>;
    // Update all docuemnts which matches the provdied conditions
    bulkUpdate<I>(criteria: FilterQuery<D>, update: UpdateQuery<D>, options?: any): Promise<T[]>;
    delete(criteria: FilterQuery<D>): Promise<boolean>;
    
    // Bulk Create provided documents
    bulkCreate<I>(data: I[]): Promise<T[]>;
    // Delete all documets which matches the criteria
    bulkDelete(criteria: FilterQuery<D>): Promise<number>;
}
export enum ISortEnum {
    Asc = 'ASC',
    Desc = 'DESC'
}
export type ISort = {
    key: Scalars['String'];
    value: ISortEnum;
};
export interface GetAllArgs<T> {
    criteria?: FilterQuery<T>;
    sort?: ISort;
    skip?: number;
    limit?: number;
    selectedFields?: string;
}
Example
If you have a document named User you should define both Repository class and interface against it like the following
export type IUserRepository = IBaseRepository<IUser>
import { BaseRepository, IMongoOptions } from '@common-stack/store-mongo';
import { inject, injectable, optional } from 'inversify';
import { Connection } from 'mongoose';
import { logger as Logger } from '@cdm-logger/server/lib/logger';
import { UserModelFunc } from '../models';
@injectable()
export class UserRepository extends BaseRepository<IUser> implements IUserRepository {
    constructor(
        @inject('MongoDBConnection')
            db: Connection,
        @inject('Logger')
            logger: typeof Logger,
        @inject('IMongoOptions')
        @optional()
            options?: IMongoOptions,
    ) {
        super(UserModelFunc, db, logger, options);
    }
}
After creating the Repository class, the next step is to register it with the Dependency Injection so It can be injected easily everywhere in the app.
After creating repository, the next step is to add Service