Skip to content

Commit

Permalink
Merge pull request #38 from zhumeisongsong/feature/user-infrastructure
Browse files Browse the repository at this point in the history
feat: ✨ mongoose of user infrastructure
  • Loading branch information
zhumeisongsong authored Nov 19, 2024
2 parents ba557c0 + d7d0e64 commit d035d96
Show file tree
Hide file tree
Showing 14 changed files with 198 additions and 7 deletions.
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,14 @@ You can use `npx nx list` to get a list of installed plugins. Then, run `npx nx

## Architecture

| Layer | Description |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| dto(presentation) | Define DTOs for GraphQL schema. |
| resolver(presentation) | Define GraphQL schema and resolver. |
| use-case(application) | Define business use cases and encapsulate business logic. |
| entity(domain) | Define core business entities and business rules.<br/> Maintain entity independence from database and framework. |
| repository(domain) | Interfaces (or abstract classes), which define methods for manipulating data without concern for specific database implementations. <br/>By defining this interface, we can decouple database access: the specific details of data access will be done by implementation classes, such as specific implementations using tools like Mongoose, TypeORM, Prisma, and so on. |
| Layer | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| dto(presentation) | Define DTOs for GraphQL schema. |
| resolver(presentation) | Define GraphQL schema and resolver. |
| mongoose(infrastructure) | Implements the repository interfaces defined in the domain layer using Mongoose as the ODM (Object Document Mapper). <br/>Includes Mongoose Schema definitions, database connection management, and concrete implementations of repository interfaces (e.g., MongooseUserRepository). |
| use-case(application) | Define business use cases and encapsulate business logic. |
| entity(domain) | Define core business entities and business rules.<br/> Maintain entity independence from database and framework. |
| repository(domain) | Interfaces (or abstract classes), which define methods for manipulating data without concern for specific database implementations. <br/>By defining this interface, we can decouple database access: the specific details of data access will be done by implementation classes, such as specific implementations using tools like Mongoose, TypeORM, Prisma, and so on. |

## Useful links

Expand Down
7 changes: 7 additions & 0 deletions libs/users/infrastructure/mongoose/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# users-infrastructure-mongoose

This library was generated with [Nx](https://nx.dev).

## Running unit tests

Run `nx test users-infrastructure-mongoose` to execute the unit tests via [Jest](https://jestjs.io).
3 changes: 3 additions & 0 deletions libs/users/infrastructure/mongoose/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const baseConfig = require('../../../../eslint.config.js');

module.exports = [...baseConfig];
10 changes: 10 additions & 0 deletions libs/users/infrastructure/mongoose/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export default {
displayName: 'users-infrastructure-mongoose',
preset: '../../../../jest.preset.js',
testEnvironment: 'node',
transform: {
'^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
},
moduleFileExtensions: ['ts', 'js', 'html'],
coverageDirectory: '../../../../coverage/libs/users/infrastructure/mongoose',
};
9 changes: 9 additions & 0 deletions libs/users/infrastructure/mongoose/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "users-infrastructure-mongoose",
"$schema": "../../../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "libs/users/infrastructure/mongoose/src",
"projectType": "library",
"tags": [],
"// targets": "to see all targets run: nx show project users-infrastructure-mongoose --web",
"targets": {}
}
2 changes: 2 additions & 0 deletions libs/users/infrastructure/mongoose/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './lib/mongoose-user.repository';
export * from './lib/user.schema';
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Test, TestingModule } from '@nestjs/testing';
import { getModelToken } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { MongooseUserRepository } from './mongoose-user.repository';
import { UserDocument } from './user.schema';
import { User } from '@user/domain';

describe('MongooseUserRepository', () => {
let repository: MongooseUserRepository;
let userModel: Model<UserDocument>;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
MongooseUserRepository,
{
provide: getModelToken(UserDocument.name),
useValue: {
findById: jest.fn(),
},
},
],
}).compile();

repository = module.get<MongooseUserRepository>(MongooseUserRepository);
userModel = module.get<Model<UserDocument>>(getModelToken(UserDocument.name));
});

it('should return a user when found', async () => {
const userDoc = { id: '1', name: 'John Doe' };
jest.spyOn(userModel, 'findById').mockReturnValue({
exec: jest.fn().mockResolvedValue(userDoc),
} as any);

const user = await repository.findById('1');
expect(user).toEqual(new User('1', 'John Doe'));
});

it('should return null when user is not found', async () => {
jest.spyOn(userModel, 'findById').mockReturnValue({
exec: jest.fn().mockResolvedValue(null),
} as any);

const user = await repository.findById('1');
expect(user).toBeNull();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { InjectModel } from '@nestjs/mongoose';
import { User, UserRepository } from '@user/domain';
import { Model } from 'mongoose';

import { UserDocument } from './user.schema';

export class MongooseUserRepository implements UserRepository {
constructor(
@InjectModel(UserDocument.name) private userModel: Model<UserDocument>,
) {}

async findById(id: string): Promise<User | null> {
const userDoc = await this.userModel.findById(id).exec();
return userDoc ? new User(userDoc.id, userDoc.name) : null;
}
}
36 changes: 36 additions & 0 deletions libs/users/infrastructure/mongoose/src/lib/user.schema.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Test, TestingModule } from '@nestjs/testing';
import { getModelToken } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { UserDocument } from './user.schema';

describe('UserDocument', () => {
let userModel: Model<UserDocument>;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
{
provide: getModelToken(UserDocument.name),
useValue: Model,
},
],
}).compile();

userModel = module.get<Model<UserDocument>>(getModelToken(UserDocument.name));
});

it('should be defined', () => {
expect(userModel).toBeDefined();
});

// it('should require name field', () => {
// const user = new userModel();
// const error = user.validateSync();
// expect(error.errors['name']).toBeDefined();
// });

// it('should create a user with a name', () => {
// const user = new userModel({ name: 'John Doe' });
// expect(user.name).toBe('John Doe');
// });
});
10 changes: 10 additions & 0 deletions libs/users/infrastructure/mongoose/src/lib/user.schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';

@Schema()
export class UserDocument extends Document {
@Prop({ required: true })
name!: string;
}

export const UserSchema = SchemaFactory.createForClass(UserDocument);
22 changes: 22 additions & 0 deletions libs/users/infrastructure/mongoose/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"extends": "../../../../tsconfig.base.json",
"compilerOptions": {
"module": "commonjs",
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noPropertyAccessFromIndexSignature": true
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.spec.json"
}
]
}
11 changes: 11 additions & 0 deletions libs/users/infrastructure/mongoose/tsconfig.lib.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "commonjs",
"outDir": "../../../../dist/out-tsc",
"declaration": true,
"types": ["node"]
},
"exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"],
"include": ["src/**/*.ts"]
}
14 changes: 14 additions & 0 deletions libs/users/infrastructure/mongoose/tsconfig.spec.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../../dist/out-tsc",
"module": "commonjs",
"types": ["jest", "node"]
},
"include": [
"jest.config.ts",
"src/**/*.test.ts",
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
}
3 changes: 3 additions & 0 deletions tsconfig.base.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
],
"@user/domain": ["libs/user/domain/src/index.ts"],
"@users/application": ["libs/users/application/src/index.ts"],
"@users/infrastructure-mongoose": [
"libs/users/infrastructure/mongoose/src/index.ts"
],
"@users/presentation-dto": ["libs/users/presentation/dto/src/index.ts"],
"@users/presentation-resolver": [
"libs/users/presentation/resolver/src/index.ts"
Expand Down

0 comments on commit d035d96

Please sign in to comment.