Java - 我应该在哪里放置我的域对象逻辑?

时间:2013-11-25 06:05:13

标签: java spring jpa architecture

我正在开发一个java-spring项目,我有一个包gr.serafeim.domain,其中包含我所有的域类(例如,学生,学校等 - 它们是具体的类)。所有这些都通过JPA注释与它们之间有关系。到目前为止一切都很好,但现在我需要为这些类实现需要查询数据库以获得结果的方法。

我应该如何实施这些方法?我的第一选择是将放在域类中,但为了做到这一点,我需要在所有域类中包含对数据存储库的引用。我不太喜欢这个 - 这是一个很好的设计选择吗?我应该实现我的域类将实现的接口吗?您能否提出更好的解决方案 - 在这种情况下通常的做法是什么?

TIA

2 个答案:

答案 0 :(得分:12)

我的回答:不,不要将存储库的引用放入域模型中。将它们放入业务服务中。并且根本不管理域中的任何安全性。安全性是指用例,而不是域逻辑,因此安全性是通过域进行的。

我不同意Sandhu。我使用以下架构:

  1. 模型类。他们没有获得所有东西的吸气剂/安装者。这取决于模型逻辑。否则你会得到模型,你很容易打破一致性。或者许多不明显的事情在哪里。假设您有User.registrationDate字段。构造新的User对象时,不要忘记手动注册registrationDate字段。所以,只需在构造函数中放置registrationDate初始化并删除setter!
  2. 模型中的存储库界面。假设您有一些依赖于现有存储对象的业务逻辑。您无法明确地将域逻辑引用到基础结构依赖项(如JPA,Hibernate,JDBC等)中。因此,您可以从接口查询此存储的对象。
  3. 商业服务(可选)。它们实现了一些复杂的逻辑,涉及许多不同的实体,不包括安全和事务管理。你的问题是关于它的。是的,如果您需要查询域逻辑中的实体,请将查询放入存储库并从业务服务中调用它。
  4. 基础架构包内的存储库实现。使用JPA或mockito或其他任何方式实现存储库接口。它们也不包括安全性和交易。
  5. 应用程序服务(可选)。如果与基础设施或安全检查存在一些复杂的交互。
  6. 远程外观界面。客户端和服务器仅通过远程外观接口进行通信。
  7. 远程外观实现(控制器)。将厚实体对象转换为精简DTO(数据传输对象)。所有事务划分和安全性都在这里(通常使用注释)。
  8. 这种方法符合DDD风格,由Martin Fowler描述。我认为JPA在大多数现代项目中都很受欢迎。它不是用作持久性提供程序,而是用作活动记录。活动记录不是域模型实现模式,而是数据库抽象模式。因此,如果您真的想要事务脚本方法,请使用一些活动记录库或类似MyBatis而不是重量级JPA提供程序。

    另外我不明白DAO的需要。 JPA提供者自己做数据抽象,不是吗?数据抽象也不是关于模型,而是关于基础设施。那么为什么DAO放在模型上呢?如果你真的需要DAO,你应该将它置于模型之下(我认为是存储库实现)。

    正确用法示例:

    package my.example.model;
    
    @Entity
    public class User {
        @Id
        @GeneratedValue
        private Integer id;
        private String login;
        private String password;
        @Temporal(TemporalType.TIMESTAMP)
        private Date registrationDate;
    
        User() {
            // for persistence provider only
        }
    
        public User(String login, String password) {
            this.login = login;
            this.password = hashPassword(password);
            this.registrationDate = new Date();
        }
    
        public String getLogin() {
            return login;
        }
    
        public String setPassword(String password) {
            this.password = hashPassword(password);
        }
    
        public boolean matchPassword(String password) {
            return this.password.equals(hashPassword(password));
        }
    
        public Date getRegistrationDate() {
            return registrationDate;
        }
    
        private static String hashPassword(String password) {
            try {
                MessageDigest digest = MessageDigest.getInstance("sha-1");
                StringBuilder sb = new StringBuilder();
                byte[] bytes = digest.digest(password.getBytes(charset));
                for (byte b : bytes) {
                    sb.append(Character.forDigit((b >>> 4) & 0xF, 16)).append(Character.forDigit(b & 0xF, 16));
                }
                return sb.toString();
            } catch (NoSuchAlgorithmException e) {
                throw new AssertionError(e);
            }
        }
    }
    
    package my.example.model;
    
    public interface UserRepository {
        User findByLogin(String login);
    
        User findBySurrogateId(int id);
    
        Integer getSurrogateId(User user);
    
        boolean contains(User user);
    
        void add(User user);
    
        void delete(User user);
    }
    
    package my.example.infrastructure;
    
    @Component
    public class PersistentUserRepository implements UserRepository {
        @PersistenceContext
        private EntityManager em;
    
        public void setEntityManager(EntityManager em) {
            this.em = em;
        }
    
        @Override public User findByLogin(String login) {
            // I'd use QueryDSL here
            QUser qusr = new QUser("usr");
            return new JPAQuery(em)
                    .from(qusr)
                    .where(qusr.login.eq(login))
                    .singleResult(qusr);
        }
    
        @Override public User findBySurrogateId(int id) {
            return em.find(User.class, id);
        }
    
        @Override public Integer getSurrogateId(User user) {
            return (Integer)em.getEntityManagerFactory().getPersistenceUnitUtil().getIdentity(user);
        }
    
        @Override public boolean contains(User user) {
            return em.contains(user);
        }
    
        @Override public void add(User user) {
            em.persist(user);
        }
    
        @Override public void delete(User user) {
            em.remove(user);
        }
    }
    
    package my.example.facade;
    
    public interface UserRemoteFacade {
        UserDTO getUser(String login);
    
        UserDTO getUser(int id);
    
        void changePassword(int userId, String newPassword);
    
        void registerUser(String login, String password) throws LoginOccupiedException;
    
        boolean authenticate(String login, String password);
    }
    
    package my.example.facade;
    
    public class UserDTO implements Serializable {
        private int id;
        private String login;
        private Date registrationDate;
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getLogin() {
            return login;
        }
    
        public void setLogin(String login) {
            this.login = login;
        }
    
        public Date getRegistrationDate() {
            return registrationDate;
        }
    
        public void setRegistrationDate(Date registrationDate) {
            this.registrationDate = registrationDate;
        }
    }
    
    package my.example.server;
    
    @Transactional @Component
    public class UserRemoteFacadeImpl imlements UserRemoteFacade {
        private UserRepository repository;
        private Security security;
    
        @Autowired
        public UserRemoteFacadeImpl(UserRepository repository, Security security) {
            this.repository = repository;
            this.security = security;
        }
    
        @Override public UserDTO getUser(String login) {
            return mapUser(repository.findByLogin(login));
        }
    
        @Override public UserDTO getUser(int id) {
            return mapUser(repository.findBySurrogateId(id));
        }
    
        private UserDTO mapUser(User user) {
            if (user != security.getCurrentUser()) {
                security.checkPermission("viewUser");
            }
            UserDTO dto = new UserDTO();
            dto.setId(repository.getSurrogateId(user));
            dto.setLogin(user.getLogin());
            dto.setRegistrationDate(user.getRegistrationDate());
            return dto;
        }
    
        @Override public void changePassword(int userId, String newPassword) {
            User user = repository.findByLogin(login);
            if (user != security.getCurrentUser()) {
                security.checkPermission("changePassword");
            }
            user.setPassword(newPassword);
        }
    
        @Override public void registerUser(String login, String password) throws LoginOccupiedException {
            if (repository.findByLogin(login) != null) {
                throw new LoginOccupiedException(login);
            }
            User user = new User(login, password);
            repository.add(user);
        }
    
        @Override public boolean authenticate(String login, String password) throws LoginOccupiedException {
            User user = repository.findByLogin(login);
            return user != null && user.matchPassword(password);
        }
    }
    

    另见此项目:http://dddsample.sourceforge.net/

答案 1 :(得分:5)

实现Spring的最佳方法是在项目中包含以下组件:

  1. 模型类(@Entity) - 完全是您的域类
  2. Dao Interfaces
  3. Dao Implemetations(@Repository
  4. 服务接口
  5. 服务实施(@Service
  6. 控制器类(@Controller
  7. 2& 3形成Persistence Layer和4& 5形成Service Layer

    示例:

    模型类

    @Entity
    public class User implements Serializable {
    
        private static final long serialVersionUID = -8034624922386563274L;
        @Id
        @GeneratedValue
        @Column(name = "id")
        private int id;
        @Column(name = "name")
        private String name;
    
        public int getId() {
            return id;
        }
    
        public void setId(final int id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(final String name) {
            this.name = name;
        }
    
    }
    

    Dao界面

    public interface UserDao {
        public User getUser(String username);
    }
    

    Dao实施

    @Repository
    public class UserDaoImpl implements UserDao {
    
        @Autowired
        private SessionFactory sessionFactory;
    
        private Session openSession() {
            return sessionFactory.getCurrentSession();
        }
    
        @Override
        public User getUser(String username) {
            List<User> userList = new ArrayList<User>();
            Query query = openSession().createQuery(
                    "from User u where u.username = :username");
            query.setParameter("username", username);
            userList = query.list();
            if (userList.size() > 0)
                return userList.get(0);
            else
                return null;
        }
    }
    

    服务界面

    public interface UserService {
        public User getUser(String username);
    }
    

    服务实施

    @Service
    @Transactional
    public class UserServiceImpl implements UserService {
        @Autowired
        private UserDao userDao;
    
        @Override
        public User getUser(final String username) {
            return userDao.getUser(username);
        }
    }