`
taowen
  • 浏览: 195486 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

贫血的Domain Model

阅读更多
好老的话题啦。拿出来炒炒冷饭。各位见谅。
——————————————————————
Domain Model贫血是说属于Domain Model的逻辑没有放在Domain Model中。那是哪些逻辑没有放到Domain Model中,从而导致贫血一说呢?原因有很多,但是我认为最主要是Service中的那些逻辑。而这些逻辑又有一个共同的特点就是依赖于DAO,或者说需要查询数据库。Robbin的帖子:http://www.iteye.com/topic/57075,举了一个很好的例子。我取其中的一个部分在这里做演示用。

public class Employee {
    private Set<Task> tasks = new HashSet<Task>();
}


public class Task {
    private String name;
    private Employee owner;
    private Date startTime;
    private Date endTime;
}


这是一个很简单的一对多的关系。现在要查找指定员工的处理中的任务。如果忽略数据库的存在,我想大部分的同志都会这么实现:

public class Employee {
    private Set<Task> tasks = new HashSet<Task>();
    public Set<Task> getProcessingTask() {
       ...
    }
}


这也符合OO数据隐藏的基本原则。但是如果有数据库存在,怎么写就不那么容易决定了。如果没有Hibernate这样的ORM。那肯定是:

public class TaskDAO {
   public Set<Task> getProcessingTasks(Employee employee) {
      ...//sql
   }
}


那我觉得,这就导致了Domain Model的失血。因为没有数据库的时候,这这个方法本来应该在Employee上的,而不是在DAO上的。
如果有Hibernate呢?是不是我就可以把这段代码写到Employee里面去呢?

@Entity
public class Employee {
    @OneToMany
    private Set<Task> tasks = new HashSet<Task>();
    public Set<Task> getProcessingTask() {
       ...
    }
}


还是有问题。因为访问tasks的时候,Hibernate会去加载数据。getProcessingTask会便利所有的task。如果task的数量很多,这降极大的影响性能。所以为了能够享受到关系数据库查询速度的好处,我们要还要利用SQL。于是DAO又再次地找到了自己的位置。那么怎么解决这个问题呢?在http://www.iteye.com/topic/57075的回帖中nihongye同学提出了一个解决方案。本质来说就是不让hibernate来映射tasks,改由查询来获得。加上Spring支持的@Configurable标记,我们可以把代码写成这样

@Entity
@Configurable
public class Employee {
    private TaskDao dao;
    public Set<Task> getProcessingTask() {
        return dao.getProcessingTask(this);
    }
    public void setTaskDao(TaskDao dao) {
        this.dao = dao;
    }
}


我们当然还可以把TaskDao替换成变的形式。比如http://www.iteye.com/topic/65406里firebody提到的那样。但是本质上来说,都是让Employee能够直接去使用Hibernate做查询。但是坏处是给Domain纯净分子的口实。虽然,我认为和ActiveRecord类似,entity绑定在数据库上没啥不好。另外一个缺点就是,要么仍然有一个Dao来封装查询逻辑的实现,要么Employee的实现中出现太多的hibernate api,而且写法复杂。这也就是Robbin一再强调,ActiveRecord那样的api在Java世界中不是不可以,而是实现复杂难度高的原因。注入可以解决问题,但是对Hibernate的依赖强而且写法丑陋。
那么有没有更优美的方案呢?有:

public class Employee {
    private RichSet<Task> tasks = new DefaultRichSet<Task>();
    public RichSet<Task> getProcessingTasks() {
        return tasks.find("startTime").le(new Date()).find("endTime").isNull();
    }
...
}



RichSet是我自己编造的一个名字。它是一个”rich“的set。其实就是附加了一些find,sort,sum之类的操作。

public interface RichSet<T> extends Set<T> {
    Finder<RichSet<T>> find(String expression);
    int sum(String expression);
}


DefaultRichSet是这些附加操作的内存版本的实现。这个能解决问题么?还是不能,这时候getProcessingTasks的时候,richSet还是去遍历内部的_tasks,然后把结果过滤出来。而且,hibernate还拒绝接受这样set。为了让hibernate能够接受RichSet,我们需要这么写配置文件。

<hibernate-mapping default-access="field" package="net.sf.ferrum.example.domain">
    <class name="Employee">
        <tuplizer entity-mode="pojo" class="net.sf.ferrum.RichEntityTuplizer"/>
        <id name="id">
            <generator class="native"/>
        </id>
        <property name="name"/>
        <property name="salary"/>
        <many-to-one name="department"/>
        <set name="tasks" cascade="all" inverse="true" lazy="true">
            <key/>
            <one-to-many class="Task" />
        </set>
    </class>
</hibernate-mapping>


通过指定RichEntityTuplizer,我们可以控制Hibernate的动态增强过程。

public class RichEntityTuplizer extends PojoEntityTuplizer {
    public RichEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappedEntity) {
        super(entityMetamodel, mappedEntity);
    }

    protected Setter buildPropertySetter(final Property mappedProperty, PersistentClass mappedEntity) {
        final Setter setter = super.buildPropertySetter(mappedProperty, mappedEntity);
        if (!(mappedProperty.getValue() instanceof org.hibernate.mapping.Set)) {
            return setter;
        }
        return new Setter() {
            public void set(Object target, Object value, SessionFactoryImplementor factory) throws HibernateException {
                Object wrappedValue = value;
                if (value instanceof Set) {
                    HibernateRepository repository = new HibernateRepository();
                    repository.setSessionFactory(factory);
                    wrappedValue = new HibernateRichSet((Set) value, repository, getCriteria(mappedProperty, target));
                }
                setter.set(target, wrappedValue, factory);
            }

            public String getMethodName() {
                return setter.getMethodName();
            }

            public Method getMethod() {
                return setter.getMethod();
            }
        };
    }
}


这样,tasks就不再是DefaultRichSet了。Hibernate会尝试去增强为PersisentSet,但是被RichEntityTuplizer改写为增强HibernateRichSet了。这样就形成了HibernateRichSet -> PersisentSet -> DefaultRichSet -> HashSet 的包含关系。

当用户尝试在tasks上做find的时候,就不再是DefaultRichSet来做collection遍历了,而是HibernateRichSet去拼装一个DetachedCriteria。最后当用户在查询的结果上取size()或者取具体元素的时候,这个criteria被拿去求值。

通过使用RichSet,domain model具有了对自身进行查询的能力。更重要的是,这种能力的获得,不是通过把Hibernate session注入到domain model中。domain仍然是纯净的,没有依赖于数据库的东西。而且domain是可以脱离容器使用的。new Employee出来就可以直接使用,测试。区别只是经过repository增强的entity会使用sql,而transient的entity所有的查询都是通过遍历实现的。

没有了DAO之后,Domain Model是不是能够摆脱贫血的困扰呢?这个还需要观察。不过我认为至少是向前迈了一步了。
  • ferrum.zip (90.6 KB)
  • 描述: 新版代码,废除了手工的增强,改由Tuplizer来搭Hibernate的顺风车。
  • 下载次数: 302
分享到:
评论
17 楼 Feiing 2008-05-10  
没了解过 rich domain object, 问个简单问题

   public RichSet<Task> getProcessingTasks() {  
        return tasks.find("startTime").le(new Date()).find("endTime").isNull();  
    } 


对这个方法, unit test 怎么写 ?
16 楼 taowen 2008-05-10  
coolnight 写道

切 前面的帖子早就看过了
在我看来结论本就很清晰 怎么简单直观怎么来而已
ruby的语言让 rich domain model 对rails比较适合而已

想在java里面也一样,那就用jruby/grails之类去好了,偏要做不适合的事情, 何苦来由?

robbin的帖子里面已经把两种的优劣比较清楚的说明白了




java的确是有一些限制,但是还是做得到的。robbin其实也在期待一个java的简洁版本的答案。我相信,我的解决方案比现有的都要简洁。
15 楼 taowen 2008-05-10  
pig345 写道
DomainObject为什么不能直接用JPA呢,就像使用java.util一样?
再再次提起老帖子
《RichDomainObject的架构设计中,是否可以抛弃DAO?》
http://pig345.iteye.com/blog/79822

给domain对象注入session或者dao最常见的两个反面回答是:形成循环依赖。Domain对象无法脱离数据库存在。但是我并不完全认同这些说法。个人认为Ruby的ActiveRecord其实就是这么干的,而且用得还挺好。但是经过我做过这个实验,我相信,不用DAO,也可以活得挺好,甚至用起来比DAO还方便。
14 楼 coolnight 2008-05-10  

切 前面的帖子早就看过了
在我看来结论本就很清晰 怎么简单直观怎么来而已
ruby的语言让 rich domain model 对rails比较适合而已

想在java里面也一样,那就用jruby/grails之类去好了,偏要做不适合的事情, 何苦来由?

robbin的帖子里面已经把两种的优劣比较清楚的说明白了



13 楼 Norther 2008-05-09  
coolnight 写道

我就搞不明白, 为什么明明很简单清晰的 DAO 类中的方法一定要使用那么多晦涩的技术
把它们搞成所谓的 rich domain model 呢?

脱裤子放屁?



不明白就要好好学习,好好看看以前的帖子。
12 楼 coolnight 2008-05-09  

我就搞不明白, 为什么明明很简单清晰的 DAO 类中的方法一定要使用那么多晦涩的技术
把它们搞成所谓的 rich domain model 呢?

脱裤子放屁?
11 楼 pig345 2008-05-09  
DomainObject为什么不能直接用JPA呢,就像使用java.util一样?
再再次提起老帖子
《RichDomainObject的架构设计中,是否可以抛弃DAO?》
http://pig345.iteye.com/blog/79822
10 楼 taowen 2008-05-09  
还可以用linq。用linq to hibernate(www.ayende.com)。
扩展方法与引用一个静态方法没有本质上的区别。你没有办法在运行时切换扩展方法的实现。如果使用static来引入依赖是不好的,用扩展方法来引入依赖同样是不好的。在domain对象的依赖注入这个问题上,spring已经用@Configurable回答过了。我认同这种把DAO注入到domain对象中的做法,有效,简单。但是也招来“非议”。会有什么样的非议,大家心知肚明。为了尝试更纯净的无容器数据库依赖的实现(只依赖特定的collection接口),我才做了上面的实验。
9 楼 ray_linn 2008-05-09  
taowen 写道
和Linq还是不一样。Linq就是一种查询语言。你可以在colllection上做linq查询,也可以在数据库上做linq查询。但是以现在的实现而言。如果你认为tasks是collection,在Employee的task上做linq查询,那就是对collection的过滤。如果你认为tasks是数据库的表,在Employee上做linq查询,那就是sql查询。但是无法做到连接到数据库的时候做sql,不连接数据库的时候做过滤。也就是说,linq也会造成你的domain model和数据库绑定。


俺用扩展方法轻松搞定http://www.iteye.com/topic/180343
8 楼 taowen 2008-05-09  
对于动态代码增强还有什么好说的呢?现在早就不是以前JDO做静态代码增强的年代了。Hibernate用动态代理这么多年,好像也没出啥问题。我只不过是在Hibernate的动态代理的collection的基础上再往前走了一步。而且这里也没产生动态代理,只是用反射给一个private field设置了值而已。性能损失是一次性的(一次private field setter)。根本就可以忽略不计。
7 楼 ray_linn 2008-05-09  
enhance....无非就是让速度更慢而已。

天啊。。。莫非我看错了,竟然还有反射。。。
6 楼 neora 2008-05-09  
JavaEye的Java论坛板块里好久没出现这种好帖了。
5 楼 chinaboy 2008-05-09  
<div class='quote_title'><img src='../../../../../../images/smiles/icon_cry.gif' alt=''/>taowen 写道</div>
<div class='quote_div'>好老的话题啦。拿出来炒炒冷饭。各位见谅。 <br/>—————————————————————— <br/>Domain Model贫血是说,属于Domain Model的逻辑没有放在Domain Model中。那是哪些逻辑没有放到Domain Model中,导致贫血一说呢?我先树一个靶子,那些放在DAO中的SQL的逻辑就是Domain Model所缺的”血“。Robbin的帖子:<a href='../../../../../../topic/57075' target='_blank'>http://www.iteye.com/topic/57075</a>,举了一个很好的例子。我再这里重复一下。 </div>
<p><br/><br/><br/></p>
<pre name='code' class='java'>public class Employee {
    private Set tasks = new HashSet();
}
</pre>
<p><br/><br/><br/><br/><br/></p>
<pre name='code' class='java'>public class Task {
    private String name;
    private Employee owner;
      private Date startTime;
    private Date endTime;
</pre>
<p><br/><br/><br/><br/>这是一个很简单的一对多的关系。现在要查找指定员工的处理中的任务。如果忽略数据库的存在,我想大部分的同志都会这么实现: <br/><br/><br/></p>
<pre name='code' class='java'>public class Employee {
    private Set tasks = new HashSet();
    public Set getProcessingTask() {
       ...
    }
}
</pre>
<p><br/><br/><br/><br/>这也符合OO数据隐藏的基本原则。但是如果有数据库存在,怎么写就不那么容易决定了。如果没有Hibernate这样的ORM。那肯定是: <br/><br/><br/></p>
<pre name='code' class='java'>public class TaskDAO {
   public Set getProcessingTasks(Employee employee) {
      ...//sql
   }
}
</pre>
<p><br/><br/><br/><br/>那我觉得,这就导致了Domain Model的失血。因为没有数据库的时候,这这个方法本来应该在Employee上的,而不是在DAO上的。 <br/>如果有Hibernate呢?是不是我就可以把这段代码写到Employee里面去呢? <br/><br/><br/></p>
<pre name='code' class='java'>@Entity
public class Employee {
    @OneToMany
    private Set tasks = new HashSet();
    public Set getProcessingTask() {
       ...
    }
}
</pre>
<p><br/><br/><br/><br/>还是有问题。因为访问tasks的时候,Hibernate会去加载数据。getProcessingTask会便利所有的task。如果task的数量很多,这降极大的影响性能。所以为了能够享受到关系数据库查询速度的好处,我们要还要利用SQL。于是DAO又再次地找到了自己的位置。 <br/>那么有没有办法让Domain Model既能够做对自己的collection做查询,同时又兼顾效率的呢?为了探索其可能性,我写了一个类似这样的伪代码: <br/><br/><br/></p>
<pre name='code' class='java'>@Entity
public class Employee {
    @OneToMany(cascade = CascadeType.ALL)
    private Set _tasks = new HashSet();

    private RichSet tasks() {
        return new InMemoryRichSet(_tasks);
    }

    public RichSet getProcessingTasks() {
        return tasks().find("startTime").le(new Date()).find("endTime").isNull();
    }
}
</pre>
<p><br/><br/><br/><br/>RichSet是我自己编造的一个名字。它是一个”rich“的set。其实就是附加了一些find,sort,sum之类的操作。InMemoryRichSet是这些附加操作的内存版本的实现。这个能解决问题么?还是不能,这时候getProcessingTasks的时候,richSet还是去遍历内部的_tasks,然后把结果过滤出来。 <br/>为了能够用SQL替代遍历这样的土办法。我们在Employee被Repository接管的时候,对它进行增强。 <br/><br/><br/></p>
<pre name='code' class='java'>private void enhance(Object obj) {
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            if (Set.class.isAssignableFrom(field.getType())) {
                try {
                    field.setAccessible(true);
                    Set set = (Set) field.get(obj);
                    field.set(obj, new HibernateRichSet(set, dao, getCriteria(field, obj)));
                } catch (IllegalAccessException e) {
                    throw new FerrumException(e);
                }
            }
        }
}
</pre>
<p><br/><br/><br/><br/>这段代码就可以把field上的所有set都增强为HibernateRichSet。然后再让InMemoryRichSet去把职责转交给增强过的set: <br/><br/><br/></p>
<pre name='code' class='java'>public class InMemoryRichSet extends WrappedSet implements RichSet {

    private RichSet richSet;

    public InMemoryRichSet(Set set) {
        super(set);
        richSet = asRichSet(set);
    }

    public Finder&gt; find(String expression) {
        if (richSet != null) {
            return richSet.find(expression);
        }
        ...
    }

    public int sum(String expression) {
        if (richSet != null) {
            return richSet.sum(expression);
        }
        ...
    }

    private RichSet asRichSet(Set set) {
        if (set instanceof RichSet) {
            return (RichSet) set;
        }
        return null;
    }
}
</pre>
<p><br/><br/><br/><br/>然后剩下的就是HibernateRichSet去玩Hibernte的Criteria了。 <br/><br/>通过使用RichSet,domain model具有了对自身进行查询的能力。更重要的是,这种能力的获得,不是通过把Hibernate session注入到domain model中。domain仍然是纯净的,没有依赖于数据库的东西。而且domain是可以脱离容器使用的。new Employee出来就可以直接使用,测试。区别只是经过repository增强的entity会使用sql,而transient的entity所有的查询都是通过遍历实现的。 <br/><br/>没有了DAO之后,Domain Model是不是能够摆脱贫血的困扰呢?这个还需要观察。不过我认为至少是向前迈了一步了。 <br/></p>
<p> </p>
4 楼 taowen 2008-05-09  
刚才又研究了一下hibernate,发现这种对collection包装的方法可以避免。可以直接在field上写RichSet<Task> tasks = new DefaultRichSet<Task>()。但是不能用annotation了,必须用hbm文件。而且要用自己的RichEntityTuplizer。

public class RichEntityTuplizer extends PojoEntityTuplizer {
    public RichEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappedEntity) {
        super(entityMetamodel, mappedEntity);
        for (int i = 0; i < setters.length; i++) {
            Setter setter = setters[i];
            if (!(setter instanceof DirectPropertyAccessor.DirectSetter)) {
                continue;
            }
            DirectPropertyAccessor.DirectSetter directSetter = (DirectPropertyAccessor.DirectSetter) setter;
            Field field = null;
            try {
                Field declaredField = directSetter.getClass().getDeclaredField("field");
                declaredField.setAccessible(true);
                field = (Field) declaredField.get(directSetter);
            } catch (Exception e) {
                throw new FerrumException(e);
            }
            if (field.getName() == "children") {
                setters[i] = new Setter() {
                    public void set(Object target, Object value, SessionFactoryImplementor factory) throws HibernateException {
                        try {
                            Field childrenField = target.getClass().getDeclaredField("children");
                            childrenField.setAccessible(true);
                            childrenField.set(target, new InMemoryRichSet((Set)value)); // here we can use HibernateRichSet instead
                        } catch (Exception e) {
                            throw new FerrumException(e);
                        }
                    }

                    public String getMethodName() {
                        return null;
                    }

                    public Method getMethod() {
                        return null;
                    }
                };
            }
        }
    }
}


<hibernate-mapping default-access="field" package="net.sf.ferrum.example.domain">
    <class name="Parent">
        <tuplizer entity-mode="pojo"
                class="net.sf.ferrum.RichEntityTuplizer"/>
        <id name="id">
            <generator class="increment"/>
        </id>
        <set name="children" cascade="all-delete-orphan">
            <key/>
            <one-to-many class="Child"/>
        </set>
    </class>
</hibernate-mapping>


public class Parent {

    private long id;

    private RichSet<Child> children = new DefaultRichSet<Child>();

    public void addChild() {
        children.add(new Child());
    }

    public long getId() {
        return id;
    }
}
3 楼 yimlin 2008-05-09  
嗯嗯!感谢楼主贡献啊!
在公司内转了,应该对我们很有用!
2 楼 taowen 2008-05-09  
和Linq还是不一样。Linq就是一种查询语言。你可以在colllection上做linq查询,也可以在数据库上做linq查询。但是以现在的实现而言。如果你认为tasks是collection,在Employee的task上做linq查询,那就是对collection的过滤。如果你认为tasks是数据库的表,在Employee上做linq查询,那就是sql查询。但是无法做到连接到数据库的时候做sql,不连接数据库的时候做过滤。也就是说,linq也会造成你的domain model和数据库绑定。
1 楼 yimlin 2008-05-09  
怎么看怎么像DLinq。
不过楼主不容易,用java的方式实现了一把!

相关推荐

    对贫血和充血模型的理解

    特别是在面向对象编程中,贫血模型(Anemic Domain Model)和充血模型(Rich Domain Model)两种设计策略,一直被广泛讨论和应用。它们代表了不同的业务逻辑和数据关系处理方式,对系统的架构和后续维护工作有着深远...

    贫血

    为了解决贫血模型的问题,开发者可能会转向富模型(Rich Domain Model)或领域驱动设计(Domain-Driven Design, DDD)。在DDD中,业务规则和逻辑被封装到领域对象(Domain Objects)中,这些对象不仅包含数据,还...

    领域模型说明及范例代码.zip

    领域模型(Domain Model)和贫血模型(Anemic Domain Model)是两种常见的模型设计模式,它们各有特点,适用于不同的场景。本资料包旨在通过实例对比,帮助初学者理解这两种模型的区别和概念,并提供实际的Java代码...

    领域模型设计技术实战篇.docx

    - 采用领域驱动设计的实践,比如贫血模型(Anemic Domain Model)和富领域模型(Rich Domain Model),以及Repository模式来管理领域对象。 通过上述分析,我们可以看到领域模型设计的关键在于理解业务逻辑,将其...

    JAVA面试题(下).pdf

    16. 领域模型(Domain Model)和贫血模型(Anaemic Domain Model)与充血模型(Rich Domain Model)的区别在于,贫血模型将业务逻辑放在服务层,而充血模型则将业务逻辑放在领域模型的实体中。 17. 测试驱动开发...

    基于struts2 spring2.5 hibernate3的人事管理系统 源码完整包

    这个系统利用了贫血模型(Anemic Domain Model)来实现DAO层,旨在提供高效、稳定且易于维护的企业级解决方案。 首先,Struts2是一个强大的MVC(Model-View-Controller)框架,它负责处理HTTP请求,并将这些请求...

    强悍的自动生成java代码工具.rar

    例如,开发者可以决定是否生成贫血模型(Anemic Domain Model)或富模型(Rich Domain Model),是否使用MyBatis或Hibernate作为持久层框架,以及选择RESTful风格的Controller还是传统的Action风格。 此外,自动...

    多表联合分页查询(Mybatis注解).zip_9AB_mybatis_skillwoc_全注解_多表关联分页查询

    例如,使用贫血模型(Anemic Domain Model)或富领域模型(Rich Domain Model),根据项目需求选择合适的数据访问模式,以及合理地组织Mapper接口和实体类,使代码结构清晰。 总的来说,这个案例涉及了Mybatis注解...

    HCPC管理系统EJB源码

    - EJB2.0的设计通常遵循一些模式,如贫血模型(Anemic Domain Model)和富模型(Rich Domain Model)。理解这些模式可以帮助我们更好地分析HCPC管理系统中的设计决策。 8. 源码学习: - 对于"jkjkljk解决好家伙...

    EJB设计模式

    12. **贫血模型和富模型(Anemic Domain Model and Rich Domain Model)** 贫血模型将业务逻辑放在服务层,而模型对象仅包含数据。富模型则将业务逻辑内聚到域对象中,提高代码的封装性和可维护性。 通过理解和...

    struts孙卫琴.rar

    学习Struts不仅要知道如何使用,还要理解如何遵循最佳实践,如分离表现层和业务层,使用贫血模型(Anemic Domain Model),以及如何有效地组织和管理ActionForm对象。 10. **实战案例** 书中可能包含多个实战案例...

    springMVC整合mybatis时的jar

    8. **最佳实践**:在实际开发中,应遵循一定的设计原则,如贫血模型(Anemic Domain Model)和富领域模型(Rich Domain Model),以及合理的分层架构,以保持代码的可维护性和扩展性。 综上所述,"springMVC整合...

    WPF中MVVM模式原理分析与实践

    - **解决现实世界的问题**:在开发过程中,开发者需要将现实世界中的实体抽象成模型,即Domain Object。这些模型可以是简单的数据载体(贫血模型),也可以包含业务逻辑(富血模型)。无论哪种形式,模型都无法直接...

    struts例子

    使用Struts开发时,应遵循一些最佳实践,例如:明确分离业务逻辑和表现层,使用贫血模型(Anemic Domain Model),合理组织Action和ActionForm,以及充分利用拦截器来增强功能。 综上所述,Struts框架通过其强大的...

    DDD领域驱动设计day01.pdf

    1. **领域模型**(Domain Model):领域模型是业务逻辑的抽象,它包含了业务实体、值对象、领域事件、聚合、工厂、仓储等元素。这些元素共同构成了一个反映业务规则的模型。 2. **统一语言**(Ubiquitous Language...

    JavaEye论坛热点月报 总第7期

    8. **贫血模型与领域模型** - 讨论了一个简单的示例,比较了贫血模型(Anemic Domain Model)和领域模型(Domain-Driven Design)在Java应用中的应用和优缺点。 9. **Spring中的DAO与Service** - 在Spring框架中,...

    java代码生成器

    3. **贫血模型(Anemic Domain Model)**:在生成的代码中,Entity类通常只包含属性,没有业务逻辑方法,这种设计被称为贫血模型,便于保持模型的简单性和纯粹性,但可能需要额外的服务层来处理业务逻辑。...

    wcl-cqrs-example:一个简单的CQRS示例

    查询模型通常使用贫血模型(Anemic Domain Model),其中对象主要包含数据字段,不包含业务逻辑。这些模型可以映射到视图层,用于展示数据。例如,我们可能会有一个`UserReadModel`,只包含用户的基本信息,用于检索...

    struts2项目

    11. **最佳实践**:在实际项目中,应遵循一些最佳实践,例如避免在Action类中使用静态变量,使用贫血模型(Anemic Domain Model)来分离业务逻辑,以及充分利用拦截器来封装共性功能。 12. **安全性**:Struts2的...

Global site tag (gtag.js) - Google Analytics