这一篇文章我们将要谈谈常常听到的 DataMapper 这个东西,应该是有不少人在一些 ORM 的 libary 中有听到这东西。
A layer of Mappers that moves data between objects and a database while keeping them independent of each other and the mapper itself.
我个人的理解为 :
这个模式的理念在於,将『 domain 』从『 persistence layer ( 可以想成资料库存取 ) 』分离出来,也就是说你的 domain model 实体中没有所谓的资料库处理这一块。
然後他大部份的使用时机为 :
复杂的业务,且 DataMapper 都会与 Domain Layer 的 Domain Model 一起用
然後有一些重点要注意一下 :
接下来我觉得直接看范例,会比较能理解做啥。
这个范例业务是这样的 :
有个用户注册,但是需要检查该员工的公司是否已满额,并且也要 18 岁才能 ( 不能是童工啊 ),但如果是 VIP 公司则没限制。
// Domain Layer =======================================
class PersonDomain{
id: string
age: number
name: string
company: string
constructor(id:string, age: number, name: string,company: string){
this.id = id
this.age = age
this.name = name
this.company = company
}
isAdult(): boolean{
return this.age >= 18
}
isVIP(): boolean{
return ['HAHOW','GOOGLE','KKBOX'].includes(this.company)
}
}
interface IPersonMapper{
findById(id: string): PersonDomain
findByCompany(company: string): PersonDomain[]
insert(person: PersonDomain): void
update(person: PersonDomain): void
delete(person: PersonDomain): void
}
// DataSource Layer =======================================
function _mockExecuteSelectSql(sql){
return [
{
id: '1',
name: 'mark',
age: 18,
company: 'HAHOW'
}
]
}
class PersonMapper implements IPersonMapper {
findById(id: string): PersonDomain{
const resultSet: any = _mockExecuteSelectSql(`SELECT * FROM person WHERE id=${id}`)
const result = this.doLoad(resultSet)
return result[0]
}
findByCompany(company: string): PersonDomain[]{
console.log('Connect to db for find')
const resultSet: any = _mockExecuteSelectSql(`SELECT * FROM person WHERE company=${company}`)
const result = this.doLoad(resultSet)
return result
}
insert(person: PersonDomain): void{
}
update(person: PersonDomain): void{
}
delete(person: PersonDomain): void{
}
private doLoad(resultSet: any): PersonDomain[]{
const result = []
for (const data of resultSet) {
result.push(new PersonDomain(data.id, data.age, data.name, data.company))
}
return result
}
}
// Application Layer =======================================
const personMapper = new PersonMapper()
const newPerson: PersonDomain = new PersonDomain(null, 18, 'Mark', 'HAHOW')
if(!newPerson.isVIP()){
const personsInCompany: PersonDomain[] = personMapper.findByCompany('HAHOW')
if(personsInCompany.length >= 10) throw Error('公司人数已达限制')
}
if(!newPerson.isAdult()) throw Error('要成年才能注册喔')
personMapper.insert(newPerson)
DataMapper 是为了要将『 资料储存 』与 『 Domain Model 』分开而诞生,那反过来思考,如果合在一起会变成怎麽样呢 ?
我觉得比较大的问题是,很多的资料库 query 不是为了该 domain 而产生的,例如在实务上我是有为了处理 N+1 的问题来抓某些资料,或是为了其它 domain 来抓资料,但那个 domain 需要的资料与原本的 domain 不相同,这几种情况。这样如果分离,就不用被绑死在 domain。
之前开发时有需要使用到 TypeORM,然後我看了现在就有个疑问 。
TypeORM - Active Record vs Data Mapper
你点进去,然後看这一段范例码,是不是很像我们上面范例的 application 层那的程序码,然後我的疑问是 :
Repository 就是指 DataMapper 吗 ?
这个问题我们下集待续。
const userRepository = connection.getRepository(User);
// example how to save DM entity
const user = new User();
user.firstName = "Timber";
user.lastName = "Saw";
user.isActive = true;
await userRepository.save(user);
// example how to remove DM entity
await userRepository.remove(user);
// example how to load DM entities
const users = await userRepository.find({ skip: 2, take: 5 });
const newUsers = await userRepository.find({ isActive: true });
const timber = await userRepository.findOne({ firstName: "Timber", lastName: "Saw" });
事实上我还不确定要如何运用呢 ~ 因为有几个问题我还有点困惑 :
<<: DAY18 - 将档案上传到 firebase storage
这边使用的是nodejs(egg) 一张图简介一下 ELK+kafka做什麽用 (用於数据分析,lo...
您的订阅是我制作影片的动力 订阅点这里~ 影片程序码 library(naniar) data(ir...
今天我们要来教一些常用到的基本设定、包括宽高、背景颜色、文字颜色,以及inline与block的区别...
大家好,我是YIYI,今天要来聊聊我想制作的APP的规格表。 动机与目的 如同【DAY 02】所说,...
Exception 与 Interrupt Interrupt 是由内部 timer 或 I/O 装...