Skip to content

Commit 967afb7

Browse files
committedSep 11, 2021
refactor: code improvements
1 parent 81fbc09 commit 967afb7

19 files changed

+125
-63
lines changed
 

‎.eslintrc.js

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ module.exports = {
3939
'import/no-cycle': 'off',
4040
'consistent-return': 'off',
4141
'no-underscore-dangle': 'off',
42+
'max-classes-per-file': 'off',
4243

4344
// Eslint errors
4445
'no-restricted-syntax': [

‎src/infrastructure/configs/app.routes.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const usersRoot = '/user';
1+
const usersRoot = '/users';
22

33
export const routes = {
44
user: {

‎src/infrastructure/database/base-classes/typeorm.entity.base.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,16 @@ export abstract class TypeormEntityBase {
88
}
99

1010
@PrimaryColumn({ update: false })
11-
id!: string;
11+
id: string;
1212

1313
@CreateDateColumn({
1414
type: 'timestamptz',
1515
update: false,
1616
})
17-
createdAt!: Date;
17+
createdAt: Date;
1818

1919
@UpdateDateColumn({
2020
type: 'timestamptz',
2121
})
22-
updatedAt!: Date;
22+
updatedAt: Date;
2323
}

‎src/interface-adapters/interfaces/user/create.user.interface.ts

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
/* Creating interfaces like this may be useful if you have a front end
2+
web/mobile application or other microservices that need to talk to
3+
your API since you can share them as a git submodule, a npm package/
4+
library or in a monorepo.
5+
*/
16
export interface CreateUser {
27
email: string;
38
country: string;

‎src/interface-adapters/interfaces/user/find-user-by-email.interface.ts

-3
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export interface FindUsers {
2+
readonly country: string;
3+
readonly postalCode: string;
4+
readonly street: string;
5+
}

‎src/modules/user/commands/create-user/create-user.http.controller.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { createUserSymbol } from '@modules/user/user.providers';
55
import { ApiOperation, ApiResponse } from '@nestjs/swagger';
66
import { CreateUserCommand } from './create-user.command';
77
import { CreateUserService } from './create-user.service';
8-
import { CreateUserRequest } from './create-user.request.dto';
8+
import { CreateUserHttpRequest } from './create-user.request.dto';
99

1010
@Controller()
1111
export class CreateUserHttpController {
@@ -27,7 +27,7 @@ export class CreateUserHttpController {
2727
@ApiResponse({
2828
status: HttpStatus.BAD_REQUEST,
2929
})
30-
async create(@Body() body: CreateUserRequest): Promise<IdResponse> {
30+
async create(@Body() body: CreateUserHttpRequest): Promise<IdResponse> {
3131
const command = new CreateUserCommand(body);
3232

3333
const id = await this.createUser.createUser(command);

‎src/modules/user/commands/create-user/create-user.event.controller.ts ‎src/modules/user/commands/create-user/create-user.message.controller.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,18 @@ import { Inject } from '@nestjs/common';
33
import { MessagePattern } from '@nestjs/microservices';
44
import { IdResponse } from 'src/interface-adapters/dtos/id.response.dto';
55
import { CreateUserCommand } from './create-user.command';
6-
import { CreateUserRequest } from './create-user.request.dto';
6+
import { CreateUserMessageRequest } from './create-user.request.dto';
77
import { CreateUserService } from './create-user.service';
88

9-
export class CreateUserEventController {
9+
export class CreateUserMessageController {
1010
constructor(
1111
@Inject(createUserSymbol)
1212
private readonly createUser: CreateUserService,
1313
) {}
1414

15-
@MessagePattern('user.create') // <- Subscribe to microservice event
16-
async create(payload: CreateUserRequest): Promise<IdResponse> {
17-
const command = new CreateUserCommand(payload);
15+
@MessagePattern('user.create') // <- Subscribe to a microservice message
16+
async create(message: CreateUserMessageRequest): Promise<IdResponse> {
17+
const command = new CreateUserCommand(message);
1818

1919
const id = await this.createUser.createUser(command);
2020

‎src/modules/user/commands/create-user/create-user.request.dto.ts

+10-4
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,27 @@ export class CreateUserRequest implements CreateUser {
1515
})
1616
@MaxLength(320)
1717
@IsEmail()
18-
email!: string;
18+
readonly email: string;
1919

2020
@ApiProperty({ example: 'France', description: 'Country of residence' })
2121
@MaxLength(50)
2222
@IsString()
2323
@IsAlpha()
24-
country!: string;
24+
readonly country: string;
2525

2626
@ApiProperty({ example: '28566', description: 'Postal code' })
2727
@MaxLength(10)
2828
@IsAlphanumeric()
29-
postalCode!: string;
29+
readonly postalCode: string;
3030

3131
@ApiProperty({ example: 'Grande Rue', description: 'Street' })
3232
@MaxLength(50)
3333
@IsAlphanumeric()
34-
street!: string;
34+
readonly street: string;
3535
}
36+
37+
export class CreateUserHttpRequest extends CreateUserRequest
38+
implements CreateUser {}
39+
40+
export class CreateUserMessageRequest extends CreateUserRequest
41+
implements CreateUser {}

‎src/modules/user/database/user.orm-entity.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ export class UserOrmEntity extends TypeormEntityBase {
88
}
99

1010
@Column({ unique: true })
11-
email!: string;
11+
email: string;
1212

1313
@Column()
14-
country!: string;
14+
country: string;
1515

1616
@Column()
17-
postalCode!: string;
17+
postalCode: string;
1818

1919
@Column()
20-
street!: string;
20+
street: string;
2121
}

‎src/modules/user/database/user.repository.ts

+19
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { QueryParams } from 'src/core/ports/repository.ports';
1414
import { UserOrmEntity } from './user.orm-entity';
1515
import { UserRepositoryPort } from './user.repository.interface';
1616
import { UserOrmMapper } from './user.orm-mapper';
17+
import { FindUsersQuery } from '../queries/find-users/find-users.query';
1718

1819
@Injectable()
1920
export class UserRepository
@@ -58,6 +59,12 @@ export class UserRepository
5859
return false;
5960
}
6061

62+
async findUsers(query: FindUsersQuery): Promise<UserEntity[]> {
63+
const users = await this.repository.find({ where: query });
64+
65+
return users.map(this.mapper.toDomainEntity);
66+
}
67+
6168
// Used to construct a query
6269
protected prepareQuery(
6370
params: QueryParams<UserProps>,
@@ -66,6 +73,18 @@ export class UserRepository
6673
if (params.id) {
6774
where.id = params.id.value;
6875
}
76+
if (params.createdAt) {
77+
where.createdAt = params.createdAt.value;
78+
}
79+
if (params.address?.country) {
80+
where.country = params.address.country;
81+
}
82+
if (params.address?.street) {
83+
where.street = params.address.street;
84+
}
85+
if (params.address?.postalCode) {
86+
where.postalCode = params.address.postalCode;
87+
}
6988
return where;
7089
}
7190
}

‎src/modules/user/dtos/user.response.dto.ts

+2
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,5 @@ export class UserResponse extends ResponseBase implements User {
4343
})
4444
street: string;
4545
}
46+
47+
export class UserHttpResponse extends UserResponse implements User {}

‎src/modules/user/queries/find-user-by-email/find-user-by-email.http.controller.ts

-25
This file was deleted.

‎src/modules/user/queries/find-user-by-email/find-user-by-email.request.dto.ts

-14
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Body, Controller, Get } from '@nestjs/common';
2+
import { routes } from '@config/app.routes';
3+
import { UserHttpResponse } from '@modules/user/dtos/user.response.dto';
4+
import { UserRepository } from '@modules/user/database/user.repository';
5+
import { FindUsersQuery } from './find-users.query';
6+
import { FindUsersHttpRequest } from './find-users.request.dto';
7+
8+
@Controller()
9+
export class FindUsersHttpController {
10+
constructor(private readonly userRepo: UserRepository) {}
11+
12+
/* Since this is a simple query with no additional business
13+
logic involved, it bypasses application's core completely
14+
and retrieves user directly from repository.
15+
*/
16+
@Get(routes.user.root)
17+
async findUsers(
18+
@Body() request: FindUsersHttpRequest,
19+
): Promise<UserHttpResponse[]> {
20+
const query = new FindUsersQuery(request);
21+
const users = await this.userRepo.findUsers(query);
22+
23+
/* Returning Response classes which are responsible
24+
for whitelisting data that is sent to the user */
25+
return users.map(user => new UserHttpResponse(user));
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Query is a plain object with properties
2+
export class FindUsersQuery {
3+
constructor(props: FindUsersQuery) {
4+
this.country = props.country;
5+
this.postalCode = props.postalCode;
6+
this.street = props.street;
7+
}
8+
9+
readonly country: string;
10+
11+
readonly postalCode: string;
12+
13+
readonly street: string;
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { ApiProperty } from '@nestjs/swagger';
2+
import { MaxLength, IsString, IsAlpha, IsAlphanumeric } from 'class-validator';
3+
import { FindUsers } from 'src/interface-adapters/interfaces/user/find-users.interface';
4+
5+
export class FindUsersRequest implements FindUsers {
6+
@ApiProperty({ example: 'France', description: 'Country of residence' })
7+
@MaxLength(50)
8+
@IsString()
9+
@IsAlpha()
10+
readonly country: string;
11+
12+
@ApiProperty({ example: '28566', description: 'Postal code' })
13+
@MaxLength(10)
14+
@IsAlphanumeric()
15+
readonly postalCode: string;
16+
17+
@ApiProperty({ example: 'Grande Rue', description: 'Street' })
18+
@MaxLength(50)
19+
@IsAlphanumeric()
20+
readonly street: string;
21+
}
22+
23+
export class FindUsersHttpRequest extends FindUsersRequest
24+
implements FindUsers {}

‎src/modules/user/user.module.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,21 @@ import { TypeOrmModule } from '@nestjs/typeorm';
33
import { UserOrmEntity } from './database/user.orm-entity';
44
import { UserRepository } from './database/user.repository';
55
import { CreateUserHttpController } from './commands/create-user/create-user.http.controller';
6-
import { FindUserByEmailHttpController } from './queries/find-user-by-email/find-user-by-email.http.controller';
76
import { DeleteUserHttpController } from './commands/delete-user/delete-user.controller';
87
import {
98
createUserCliLoggerProvider,
109
createUserProvider,
1110
removeUserProvider,
1211
} from './user.providers';
1312
import { CreateUserCliController } from './commands/create-user/create-user.cli.controller';
13+
import { FindUsersHttpController } from './queries/find-users/find-users.http.controller';
1414

1515
@Module({
1616
imports: [TypeOrmModule.forFeature([UserOrmEntity])],
1717
controllers: [
1818
CreateUserHttpController,
1919
DeleteUserHttpController,
20-
FindUserByEmailHttpController,
20+
FindUsersHttpController,
2121
],
2222
providers: [
2323
UserRepository,

‎tsconfig.json

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"emitDecoratorMetadata": true,
88
"experimentalDecorators": true,
99
"allowSyntheticDefaultImports": true,
10+
"strictPropertyInitialization": false,
1011
"target": "es2019",
1112
"sourceMap": true,
1213
"outDir": "./dist",

0 commit comments

Comments
 (0)
Failed to load comments.