IMapper
The mapper's function is to translate and/or convert a domain object into a persistence object
There are two methods: toDomain
and toPersistence
the first one receives as argument a persistence object and convert it to a domain object.
// Mapper to be injected on repository.
export class UserModelToDomainMapper implements TMapper<Model, User> {
//
map(model: Model): Result<User> {
const nameOrError = UserNameValueObject.create(model.userName);
const emailOrError = EmailValueObject.create(model.userEmail);
const passOrError = PasswordValueObject.create(model.userPassword);
const birthOrError = BirthdayValueObject.create(model.userBirthDay);
const observer = ChangesObserver.init<unknown>();
observer.add(nameOrError);
observer.add(emailOrError);
observer.add(passOrError);
observer.add(birthOrError);
const result = observer.getResult();
if (result.isFailure) {
return Result.fail(result.errorValue());
}
return User.create({
ID: DomainId.create(model.id),
userName: nameOrError.getResult(),
userEmail: emailOrError.getResult(),
userPassword: passOrError.getResult(),
userBirthDay: birthOrError.getResult(),
createdAt: model.createdAt,
updatedAt: model.updatedAt,
isDeleted: model.isDeleted,
});
}
}
// What about Domain to Persistence Conversion ???
// use your domain instance toObject method. e.g: user.toObject();
// OR
export class UserDomainToModelMapper implements TMapper<User, Model> {
//
map(domain: User): Result<Model> {
const model: Model = {
id: domain.id.uid,
userName: domain.userName.toObject(),
userEmail: domain.userEmail.toObject(),
userPassword: domain.userPassword.toObject(),
userBirthDay: domain.userBirthDay.toObject(),
createdAt: domain.createdAt,
updatedAt: domain.updatedAt,
isDeleted: domain.isDeleted,
};
return Result.ok(model);
}
}
Mapper with state
TMapper implements map method with state
import { State, TMapper, FactoryMethod, Result, DomainId } from 'types-ddd';
interface UserModel {
id: string;
name: string;
age: number;
createdAt?: Date;
updatedAt?: Date;
}
class UserToDomainMapper extends State<UserModel>
implements TMapper<UserModel, UserDomainEntity> {
map ( model: UserModel ): Result<UserDomainEntity> {
this.startState();
this.addState( 'age', AgeValueObject.create( model.age ) );
this.addState( 'name', NameValueObject.create( model.name ) );
const result = this.checkState();
if ( result.isFailure ) {
return Result.fail( result.error );
}
return UserDomainEntity.create( {
ID: DomainId.create(model.id),
age: this.getStateByKey<AgeValueObject>('age').getResult(),
name: this.getStateByKey<NameValueObject>('name').getResult(),
createdAt: model.createdAt,
updatedAt: model.updatedAt
})
}
}
class UserToDomainFactory extends FactoryMethod<UserModel, UserDomainEntity> {
protected create (): TMapper<UserModel, UserDomainEntity> {
return new UserToDomainMapper();
}
}
const model: UserModel = {
id: 'b082f233-0600-4359-994d-31f63ea2fa39',
age: 18,
name: 'Neo'
}
const domainEntity = UserDomainEntity.build(model, new UserToDomainFactory());
State
Generate a state for any class that extends to it
import { State } from 'types-ddd';
// state has a generic type to define possible keys
State<UserModel>
// get all keys on state
return this.getState();
// add a result to state
this.addState<AgeValueObject>('age', AgeValueObject.create(age));
// clear all data on state
this.resetState();
// starts a new state
this.startState();
// check if a key exists on state
return this.exists(key);
// get a state by key always returns a value
return this.getStateByKey<AgeValueObject>( key );
// get a state by key returns a value or undefined
return this.getStateByKey<AgeValueObject, undefined>( key );
// get a state by key returns a callback if key is not found
return this.getStateByKey<AgeValueObject>( key, callback );
// check if all state is success
return this.checkState();
// count state lenth
return this.getSize();
What if I have a possible undefined key
Example a user has a optional role on dto, but required on domain entity
import { BaseDomainEntity, State, Entity, UserNameValueObject, EmailValueObject } from 'types-ddd';
// REMEMBER THIS METHOD ON STATE
// get a state by key returns a callback if key is not found
return this.getStateByKey<AgeValueObject>( key, callback );
// AGGREGATE PROPS
interface UserProps extends BaseDomainEntity {
name: UserNameValueObject;
email: EmailValueObject;
password: PasswordValueObject;
role: RoleValueObject;
}
// AGGREGATE
class UserDomainEntity extends Entity<UserProps> {
private constructor(props: UserProps){
super(props, UserDomainEntity.name)
}
get email(): EmailValueObject {
return this.props.email;
}
get name(): UserNameValueObject {
return this.props.name;
}
get password(): PasswordValueObject {
return this.props.password;
}
get role(): RoleValueObject {
return this.props.role;
}
public static create (props: UserProps): Result<UserDomainEntity, string> {
return Result.ok(new UserDomainEntity(props));
}
}
// DTO
interface CreateUserDto {
name: string;
email: string;
password: string;
role?: 'ADMIN' | 'MEMBER';
}
// MAPPER TO BUILD A USER FROM MODEL
class UserToDomainMapper extends State<CreateUserDto>
implements TMapper<CreateUserDto, UserDomainEntity> {
// MAP METHOD
map(target: CreateUserDto): Result<UserDomainEntity> {
// START STATE
this.startState();
// ADD VALUE OBJECT TO STATE
this.addState('name', UserNameValueObject.create(target.name));
this.addState('email', EmailValueObject.create(target.email));
this.addState('password', PasswordValueObject.create(target.password));
target.role && this.addState('role', RoleValueObject.create(target.role));
// VALIDATE ALL STATE
const result = this.checkState();
if (result.isFailure) {
return Result.fail(result.error);
}
// DEFAULT ROLE
const defaultRole = RoleValueObject.create('MEMBER');
// PROPS TO USER
const role = this.getStateByKey<PasswordValueObject>('role', defaultRole).getResult();
const name = this.getStateByKey<UserNameValueObject>('name').getResult();
const email = this.getStateByKey<EmailValueObject>('email').getResult();
const password = this.getStateByKey<PasswordValueObject>('password').getResult();
const ID = DomainId.create();
return UserDomainEntity.create({ ID, name, email, password, role });
}
}
// FACTORY TO PROVIDE TO BUILDER
class UserToDomainFactory extends FactoryMethod<CreateUserDto, UserToDomainMapper> {
protected create (): TMapper<CreateUserDto, UserToDomainMapper> {
return new UserToDomainMapper();
}
}
// DTO TO CREATE A NEW USER
const dto: CreateUserDto = {
name: 'Neo';
email: 'neo@matrix.com';
password: 'M4TR1X-R355UR31T10N';
}
// BUILD A NEW DOMAIN USER FROM DTO
const domainEntity = UserDomainEntity.build(dto, new UserToDomainFactory());
// GET INSTANCE
const userDomainEntity = domainEntity.getResult();
// REVERSE: FROM DOMAIN INSTANCE TO MODEL
const model = userDomainEntity.toObject<UserModel>(new UserToModelFactory());
Last updated
Was this helpful?