
Company News
Socket Named Top Sales Organization by RepVue
Socket won two 2026 Reppy Awards from RepVue, ranking in the top 5% of all sales orgs. AE Alexandra Lister shares what it's like to grow a sales career here.
domain-repository
Advanced tools
IDomainRepository is an extension of ORM frameworks (Mongoose or TypeORM) that **automatically maps DB models into Domain models**.
IDomainRepository is an extension of ORM frameworks (Mongoose or TypeORM) that automatically maps DB models into Domain models.
Useful links:
#1. First uninstall Mongoose and MongoDb.
npm uninstall mongoose mongodb @types/mongoose @types/mongodb
#2. Install latest Mongoose.
npm install mongoose
#3. Only AFTER you have latest Mongoose installed, install domain-repository.
npm install domain-repository
If you do it the other way round, you can have problems with MongoDB BSON dependency!
#1. First uninstall TypeORM.
npm uninstall typeorm @types/typeorm
#2. Install latest TypeORM.
npm install typeorm
#3. Install domain-repository.
npm install domain-repository
Make sure you have domain models defined. Each model should be exported in two versions:
Detached (default model without id), for objects not yet persisted in the databaseAttached (with id), for already persisted objectsThis differentiation improves intellisense and debugging. You can call your models whatever you like, as long as you stick to your naming convention. We recommend the following good practices:
For example:
export type ICar = {
/* Mutable name of the car. */
name: string;
/* Mutable flag. Equals true for the single, highest ranked car in the system. */
best: boolean;
/* Readonly year of production. */
readonly yearOfProduction: number;
/* Mutable sale date. Optional means the car was not sold yet. */
sold?: Date;
};
export type ICarAttached = ICar & { id: string };
An attached model will contain:
Let's say you want to use MongoDB as your DB, and Mongoose as your ORM.
Create a new file for my DB model, for example: car.entity.ts:
Because this library uses domain mapping, this model does not have to be the same as your domain model.
export type ICarMongoEntity = {
_id: mongoose.Types.ObjectId;
name: string;
best_of_all: boolean;
readonly yearOfProduction: number;
sold?: Date;
};
Now create file car.schema.ts and define your db schema, using mongoose:
//standard MongoDb schema, typed with your db model
export const CarSchema = new Schema<ICarMongoEntity>({
name: {
type: String,
required: true
},
best_of_all: {
type: Boolean,
required: true
},
yearOfProduction: {
type: Number,
required: true
},
sold: {
type: Date,
required: false
}
});
Check strict-type-mapper npm package for more details.
import { Mapping } from 'strict-type-mapper';
import { mapToMongoObjectId } from 'domain-repository/db/mongodb';
//define mapping from domain model to db model
export const mongoCarMapping: Mapping<ICarAttached, ICarMongoEntity> = {
id: mapToMongoObjectId,
name: 'name',
best: 'best_of_all',
yearOfProduction: 'yearOfProduction',
sold: 'sold'
};
Use IDomainRepository interface in places, where you would previously use Mongoose collection or TypeORM repository. Type it explicitly with your Domain model type.
import { IDomainRepository } from 'domain-repository';
export class CarService {
constructor(private readonly carRepository: IDomainRepository<ICar, ICarAttached>) {}
public async create(car: ICar): Promise<ICarAttached> {
return this.carRepository.create(car);
}
public async findBestCar(): Promise<ICarAttached | undefined> {
return this.carRepository.findOne({ best: true });
}
}
If you only need to read or write data you can also use narrowed versions of interfaces: IReadDomainRepository or IWriteDomainRepository (SOLID's Interface segregation principle).
Here lies the greatest benefit of using IDomainRepository. You can easily test your services using MockedDbRepository implementation.
No more difficult mocking of db methods!
This way you can focus on your business code and test only that (this is one of the principal guidelines of unit testing).
import { MockedDBRepository } from 'domain-repository';
describe('CarService', () => {
const initialData: ICarAttached[] = [
{ id: '1', name: 'Volvo', best: false, yearOfProduction: 2000 },
{
id: '2',
name: 'Toyota',
best: true,
yearOfProduction: 2010,
sold: new Date()
}
];
const mockedRepository = new MockedDBRepository<ICar, ICarAttached>(initialData);
const carService = new CarService(mockedRepository);
it('should find best car', async () => {
const car = await carService.findBestCar();
expect(car).toBeDefined();
expect(car!.name).toEqual('Toyota');
});
});
Now depending on your db and ORM layer, you need to create ORM repository and pass it to our implementation of IDomainRepository.
MongoDb example:
import { MongoDbRepository } from 'domain-repository/db/mongodb';
const runMongoTest = async (): Promise<void> => {
await new Promise<void>((resolve) => {
mongoose.connect('mongodb://127.0.0.1:27017/testdb', {});
mongoose.connection.on('open', () => resolve());
});
const carRepository = new MongoDbRepository<ICar, ICarAttached, ICarMongoEntity>(
mongoose.model<ICarMongoEntity>('cars', CarSchema),
mongoCarMapping
);
const carService = new CarService(carRepository);
await carService.create({
name: 'Toyota',
best: true,
yearOfProduction: 2010,
sold: new Date()
});
const bestCar = await carService.findBestCar();
console.log(bestCar);
};
runMongoTest();
Output:
{
id: '63b8091cdd1f0c4927ca4725',
name: 'Toyota',
best: true,
yearOfProduction: 2010,
sold: 2023-01-06T11:42:20.836Z
}
MongoDB data (see best_of_all renamed property):
{
"_id": {
"$oid": "63b8091cdd1f0c4927ca4725"
},
"name": "Toyota",
"best_of_all": true,
"yearOfProduction": 2010,
"sold": {
"$date": "2023-01-06T11:42:20.836Z"
},
"__v": 0
}
Db model:
export type ICarSqlEntity = {
id: number;
name: string;
best_of_all: boolean;
readonly yearOfProduction: number;
sold?: Date;
};
Db schema and mapping:
import { Mapping } from 'strict-type-mapper';
import { mapToSqlIntId } from 'domain-repository/db/postgresql';
//you can put ! next to the properties, to prevent Typescript no-initializer warnings
@Entity('cars')
export class SqlCarEntity implements ICarSqlEntity {
@PrimaryGeneratedColumn()
readonly id!: number;
@Column('text')
name!: string;
@Column('bool')
best_of_all!: boolean;
@Column('int')
readonly yearOfProduction!: number;
@Column('text', { nullable: true })
sold?: Date;
}
export const sqlCarMapping: Mapping<ICarAttached, ICarSqlEntity> = {
id: mapToSqlIntId,
name: 'name',
best: 'best_of_all',
yearOfProduction: 'yearOfProduction',
sold: 'sold'
};
Test code:
import { PostgreSQLDbRepository } from 'domain-repository/db/postgresql';
const runPostgresTest = async (): Promise<void> => {
const dataSource = new DataSource({
type: 'postgres',
host: '127.0.0.1',
port: 5432,
database: 'mydb',
username: 'postgres',
password: 'admin',
synchronize: true, //for local testing
entities: [SqlCarEntity]
});
await dataSource.initialize();
const carRepository = new PostgreSQLDbRepository<ICar, ICarAttached, ICarSqlEntity>(
dataSource.getRepository(SqlCarEntity),
sqlCarMapping
);
const carService = new CarService(carRepository);
await carService.create({
name: 'Toyota',
best: true,
yearOfProduction: 2010,
sold: new Date()
});
const bestCar = await carService.findBestCar();
console.log(bestCar);
};
runPostgresTest();
Output:
{
id: '146',
name: 'Toyota',
best: true,
yearOfProduction: 2010,
sold: '2023-01-06T13:11:43.685+01:00'
}
PostgreSQL data (see best_of_all renamed property):
id,"name","best_of_all","yearOfProduction","sold"
146,"Toyota",True,2010,"2023-01-06T13:11:43.685+01:00"
FAQs
IDomainRepository is an extension of ORM frameworks (Mongoose or TypeORM) that **automatically maps DB models into Domain models**.
We found that domain-repository demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Company News
Socket won two 2026 Reppy Awards from RepVue, ranking in the top 5% of all sales orgs. AE Alexandra Lister shares what it's like to grow a sales career here.

Security News
NIST will stop enriching most CVEs under a new risk-based model, narrowing the NVD's scope as vulnerability submissions continue to surge.

Company News
/Security News
Socket is an initial recipient of OpenAI's Cybersecurity Grant Program, which commits $10M in API credits to defenders securing open source software.