簡體   English   中英

Java-我應該在哪里放置域對象邏輯?

[英]Java - where should I put my domain object logic?

我正在開發一個java-spring項目,我有一個gr.serafeim.domain包,其中包含我的所有領域類(例如Student,School等-它們是具體的類)。 所有這些都通過JPA注釋在它們之間具有關系。 到目前為止,一切都很好,但是現在我需要為這些類實現方法,這些方法需要查詢數據庫以獲得其結果。

我應該如何實現這些方法? 我的第一選擇是將其放在域類中,但是要做到這一點,我需要在所有域類中包括對數據存儲庫的引用。 我不太喜歡-這是一個不錯的設計選擇嗎? 是否應該實現我的域類將實現的接口? 您能提出更好的解決方案嗎?在這種情況下,通常的做法是什么?

TIA

我的回答:不,不要將對存儲庫的引用放入您的域模型中。 而是將它們放入業務服務中。 而且根本不對域進行任何安全管理。 安全性是指用例,而不是域邏輯,因此安全性放在域上。

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

  1. 模型類。 他們不會為所有事情獲得吸氣劑/助洗劑。 這取決於模型邏輯。 否則,您將獲得可以輕松破壞一致性的模型。 還是哪里有很多不明顯的東西。 假設您有User.registrationDate字段。 當您構造一個新的User對象時,您不應忘記手動輸入registrationDate字段。 因此,只需在您的構造函數中放置registrationDate初始化並刪除setter即可!
  2. 模型內部的存儲庫接口。 假設您有一些業務邏輯依賴於現有的存儲對象。 您不能從域邏輯中明確地引用諸如JPA,Hibernate,JDBC等基礎結構依賴項。因此,您可以從接口查詢此存儲的對象。
  3. 商業服務(可選)。 它們實現了一些復雜的邏輯,涉及許多不同的實體,不包括安全性和事務管理。 您的問題就是這個。 是的,如果您需要查詢域邏輯中的實體,請將查詢放入存儲庫並從您的業務服務中調用它。
  4. 基礎結構包中的存儲庫實現。 使用JPA或Mockito或其他方式實現存儲庫接口。 它們也不包含安全性和事務。
  5. 應用程序服務(可選)。 與基礎架構或安全檢查之間是否存在復雜的交互。
  6. 遠程外觀界面。 客戶端和服務器僅通過遠程外觀接口進行通信。
  7. 遠程外觀實現(控制器)。 將厚實體對象轉換為薄DTO(數據傳輸對象)。 所有事務划分和安全都在這里(通常使用注釋)。

此方法符合Martin Fowler描述的DDD樣式。 我認為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/

實施Spring的最好方法是在項目中包含以下組件:

  1. 模型類( @Entity )-確實是您的領域類
  2. 道接口
  3. 道實現( @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;
    }

}

道接口

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

實施

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM