I am developing a java-spring project and I have a packagegr.serafeim.domain
which contains all my domain classes (for instance, Student, School etc - they are concrete classes). All these have relations between them through JPA annotations. Until now everything was fine, but now I need to implement methods to these classes that would need to query the database to get their results.
How should I implement these methods ? My first choice would be to put it inside the domain classes, however in order to do that I'd need to include references to the data repositories in all my domain classes. I don't like this very much -- is this a good design choice ? Should I implement interfaces that my domain classes would implement ? Can you propose a better solution -- what is the common practice in such cases ?
TIA
The theory states that the domain objects should encapsulate their behaviour and business logic. A model which contains only data and has its logic somewhere outside is called an "anemic domain model", which is a bad thing. Also, the domain should not perform data access.
A domain class represents a table column and it allows you to handle the column value as a Java object. In the Doma framework, a domain means all the values which a data type may contain. In short, a domain class is a user defined class that can be map to a column. The use of the domain classes is optional.
Usually a domain folder contains POJOs (Plain Old Java Objects). This folder basically stores classes that might or might not be entities, but follow a common structure: fields. construstors. getters and setters.
The domain object model is based on the Decision Optimization Center Application Data Model. It provides a simple way to map tables to Java classes, columns to attributes, and foreign keys to bidirectional references.
My answher: no, don't place references to repositories into you domain models. Place them into business services instead. And don't manage any security into domain at all. Security is refered to use cases, not domain logic, so security is placed over domain.
And I disagree with Sandhu. I'd use the following architecture:
This approach conforms DDD style, described by Martin Fowler. I think that JPA is musused in most of modern projects. It is used not as persistence provider, but as active record. Active record is not domain model implementation pattern, but database abstraction pattern. So if you really want transaction script approach, use some active record library or something like MyBatis instead of heavyweight JPA provider.
Also I don't understand the need of DAO. JPA providers do data abstraction themselves, don't they? Also data abstraction is not about model, but about infrastructure. So why is DAO placed over model? If you do really need DAO, you should place it under model (into repository implementation, I suppose).
Example of right usage:
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);
}
}
Also see this project: http://dddsample.sourceforge.net/
The best way to implement Spring is to have the following components in your project:
@Entity
)- Exactly your domain classes@Repository
)@Service
)@Controller
)2 & 3 forms the Persistence Layer
and 4 & 5 forms the Service Layer
Example:
Model class
@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 Interface
public interface UserDao {
public User getUser(String username);
}
Dao Implementation
@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;
}
}
Service Interface
public interface UserService {
public User getUser(String username);
}
Service Implementation
@Service
@Transactional
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public User getUser(final String username) {
return userDao.getUser(username);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With