前言
Apache Isis是DDD方法的一个实现框架。
需求分析
宠物诊所需要记录宠物的资料,包括类型、名字、及其主人。
多个宠物可能同属于一个主人。
宠物可能更换主人。
宠物主人记录姓名,我们可以按姓名来查找宠物主人。
需求建模
根据需求,我们可以创建宠物(Pet)和宠物主人(Owner)两个实体类。宠物和主人是一对多的关系。宠物可以变更主人,当然这里我仅限于宠物来选择主人,那给宠物换主人就是宠物自身的一个行为。
创建宠物和创建宠物主人,我们需要相应业务对象来进行初始化操作。这个业务对象可以认为是一个工厂,专门用来创建不存在的实体对象。
宠物主人的查询,我们也是通过业务对象来进行实体的检索。这个业务对象可认为是一个实体仓库,我们从中检索需要的实体对象。
通过以上描述,我们建立以下的UML图形。
宠物诊所静态模型
注:由于业务规则相当简单,为了简化编程步骤,这里的宠物主人的查询和创建放到了一个类中。
程序设计
ISIS的程序设计,关注点是业务规则(Service、Action、Entity)。Service为功能项,表现为主菜单;Action为用户操作,表现为菜单项或按钮;Entity用户操作的内容或结果,表现为具体的数据表单。Service和Entity都可以承载Action。
宠物主人管理的业务实现
将宠物主人的业务对象定义为DomainService,表明此对象宠物主人操作的入口。DomainService.repositoryFor()表明,这个类也是Owner的实体仓库。Owners 在系统中对应一个菜单。
@DomainService(repositoryFor = Owner.class)public class Owners { //.... }
给这个类创建两个动作(方法),一个是创建宠物主人(Owner),另一个是用名字来查询。create方法对应创建这个动作,在界面上对应一个Create的菜单项。create参数对应创建动作要输入的内容,在界面上表现为一个输入表单。findByName同理,不过create和findByName的区别是返回不一样,前者返回所创建实体的内容(详情页),后者返回查询的实体集合(列表页)。
@MemberOrder(sequence = "2") public Owner create( final @ParameterLayout(named = "Name") String name) { final Owner obj = repository.instantiate(Owner.class); obj.setName(name); repository.persist(obj); return obj; } //endregion //region > findByName (action) @MemberOrder(sequence = "1") public List<Owner> findByName( @ParameterLayout(named = "Name") final String name) { final String nameArg = String.format(".*%s.*", name); final List<Owner> owners = repository.allMatches( new QueryDefault<>( Owner.class, "findByName", "name", nameArg)); return owners; }
这里使用了持久化服务,这里的持久化是保用的关系型数据库。
宠物管理的业务实现
同样先定义Service。
@DomainService(repositoryFor = Pet.class) public class Pets { //... }
再创建Action
public Pet create( final @ParameterLayout(named = "宠物名字") String name, final @ParameterLayout(named = "宠物类型") PetSpecies species,@ParameterLayout(named = "宠物主人") final Owner owner) { final Pet pet = repository.instantiate(Pet.class); pet.setName(name); pet.setSpecies(species); pet.setOwner(owner); repository.persist(pet); return pet; }
Pet的Action
public Pet changeOwner(Owner owner) { setOwner(owner); return this; }
一些扩展设计
对于宠物主人的选择,为了达到与相应实体类的完全匹配,更多的时候考虑采用下拉选择。ISIS提供了一些协定,比如choice,autoComplete前辍的动作,辅助数据项的选择。
public List<Owner> autoComplete0ChangeOwner(final @MinLength(1) String name) { return owners.findByName(name); }
public List<Owner> autoCompleteOwner(final @MinLength(1) String name) {
return owners.findByName(name);
}
autoComplete0ChangeOwner 方法可以理解为:autoComplete模糊,对应的参数位置: 0 ,对应的动作:ChangeOwner,
autoCompleteOwner,对应属性修改的自动完成。
运行效果
创建宠物实体
宠物列表
宠物详情
修改宠物主人