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

Ser*_*eim 8 java architecture spring jpa

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

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

TIA

Ale*_*eev 14

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

而且我不同意Sandhu.我使用以下架构:

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

这种方法符合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);
    }
}
Run Code Online (Sandbox Code Playgroud)

另见这个项目:http://dddsample.sourceforge.net/


San*_*mar 5

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

  1. Model Classes(@Entity) - 正是您的域类
  2. Dao接口
  3. Dao Implemetations(@Repository)
  4. 服务接口
  5. 服务实现(@Service)
  6. 控制器类(@Controller)

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;
    }

}
Run Code Online (Sandbox Code Playgroud)

道界面

public interface UserDao {
    public User getUser(String username);
}
Run Code Online (Sandbox Code Playgroud)

道实施

@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;
    }
}
Run Code Online (Sandbox Code Playgroud)

服务接口

public interface UserService {
    public User getUser(String username);
}
Run Code Online (Sandbox Code Playgroud)

服务实施

@Service
@Transactional
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;

    @Override
    public User getUser(final String username) {
        return userDao.getUser(username);
    }
}
Run Code Online (Sandbox Code Playgroud)