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
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:
Model classes. They don't get getters/setters for everything. It depends on model logic. Otherwise you get model where you can easily break consistency. Or where are many unobvious things. Suppose you have User.registrationDate field. When you construct a new User object, you should not forget to field registrationDate field by hands. So, just place registrationDate initialization in your constructor and remove setter!
Repository interface just inside your model. Suppose you have some business logic that depends on existing stored objects. You can't expliciltly refer from your domain logic into infrastructure dependencies like JPA, Hibernate, JDBC, etc. So you query this stored objects from interfaces.
Business services (optionally). They implement some complex logic, involving many different entities, not including security and transaction management. Your question is about it. Yes, if you need query for entities in your domain logic, place query into repository and call it from your business service.
Repository implementation inside infrastructure package. Implements repository interfaces using JPA or mockito or whatever else. They also don't include neither security nor transactions.
Application services (optionally). If there is some complex interaction with infrastructure or security checking.
Remote facade interface. Client and server communicate only through remote facade interface.
Remote facade implementation (controller). Transforms thick entity objects into thin DTOs (data transfer objects). All transaction demarcation and security is here (often using annotations).
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:
Model Classes (#Entity)- Exactly your domain classes
Dao Interfaces
Dao Implemetations (#Repository)
Service Interfaces
Service Implementations (#Service)
Controller classes (#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);
}
}
Related
This question already has an answer here:
Internal working of field injection in spring and why is it not recommended to use
(1 answer)
Closed 6 months ago.
I am very new to Spring Boot. I have created a repository, which looks like this:
public interface UserRepository extends CrudRepository<User, Long> {
List<User> findByEmail(String email);
User findById(long id);
}
user.java
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String email;
private String password;
private boolean verified;
protected User() {}
public User(String email, String password, boolean verified) {
this.email = email;
this.password = password;
this.verified = verified;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public boolean isVerified() {
return verified;
}
public void setVerified(boolean verified) {
this.verified = verified;
}
}
And now I want to inject the repository into my controller. This is how I tried it:
#RestController
public class Register {
#Autowired
private UserRepository userRepository;
#PostMapping("/register")
public User registerUser() {
return userRepository.save(new User("test#example.com", "password", true));
}
}
Is my approach correct? If so, then why do I get a warning on #Autowired that says:
Field injection is not recommended
?
You are applying field injection into your controller layer.
There is a more efficient way to do that but first, take a look at why field injection is not recommended.
Reasons
you can not define your dependent components as final(immutable).
you can not instantiate your components for test purposes.
your application may lead to circular dependencies.
your application is tightly coupled to your container.
There may be more reasons that you can search for them if you want. So now take a look at better ways to perform dependency injection.
Constructor Injection
This purpose is just to define your required dependencies as parameters to the class's constructor.
Let's say we have a class called A
#Component
Class A {}
Notice that we have to define our class as a Component so the container can use it later.
Now we want to inject class A to class B with constructor injection purpose.
Class B {
private final A a;
Public B(A a) {
this.a = a;
}
}
We successfully performed constructor injection instead of field injection.
There is another way to do dependency injection called setter injection which is useful for injecting optional dependent components.
That's it.
What I would like to achieve is to use interfaces for domain classes and generic types for service layer and be able to change the implementation of the persistence layer from current which is MongoDb to e.g. JPA. Interfaces for domain classes are necessary because of e.g different annotations for JPA and MongoDB (#Entity and #Document).
Let's look at the structure of the following demo project:
For each element of the domain model there can be three interfaces, let's explain it using the user package:
User - representation of domain object
UserDao - providing persistence layer methods
UserService - providing business logic methods
Here are interfaces for each of them:
public interface User {
String getId();
String getFirstName();
String getLastName();
List<Consent> getConsents();
Boolean getBlocked();
}
public interface UserDao <UserType extends User> {
UserType save(UserType user);
Optional<UserType> getById(String userId);
}
public interface UserService <UserType extends User> {
UserType create(String firstName, String lastName);
void addConsent(UserType user, ConsentType consentType);
}
As I mentioned earlier, current implementation of those interfaces is related to Mongo DB:
#Getter
#Setter
#Document(collection = "user")
public class MongoUser extends AbstractMongoCollection implements User {
private String firstName;
private String lastName;
private List<PojoConsent> consents;
private Boolean blocked;
void addConsent(PojoConsent consent) {
if(consents == null) {
consents = new ArrayList<>();
}
consents.add(consent);
}
#Override
public List<Consent> getConsents() {
return new ArrayList<>(consents);
}
}
#Component
public class MongoUserDao implements UserDao<MongoUser> {
private MongoUserRepository mongoUserRepository;
#Autowired
public MongoUserDao(MongoUserRepository mongoUserRepository) {
this.mongoUserRepository = mongoUserRepository;
}
#Override
public MongoUser save(MongoUser user) {
return mongoUserRepository.save(user);
}
#Override
public Optional<MongoUser> getById(String userId) {
return mongoUserRepository.findByIdAndDeletedIsFalse(userId);
}
}
#Component
public class MongoUserService implements UserService<MongoUser> {
private UserDao<MongoUser> userDao;
#Autowired
public MongoUserService(UserDao<MongoUser> userDao) {
this.userDao = userDao;
}
#Override
public MongoUser create(String firstName, String lastName) {
MongoUser user = new MongoUser();
user.setBlocked(false);
user.setFirstName(firstName);
user.setLastName(lastName);
user.setDeleted(false);
return userDao.save(user);
}
#Override
public void addConsent(MongoUser user, ConsentType consentType) {
PojoConsent pojoConsent = new PojoConsent();
pojoConsent.setActive(true);
pojoConsent.setType(consentType);
pojoConsent.setDate(LocalDateTime.now());
user.addConsent(pojoConsent);
userDao.save(user);
}
}
Ok, so what is the problem ? The problem occurs when I inject beans of type UserDao and UserService in other beans (as it happens in Spring Framework), like EntryPoint in this example (I'm aware of that there should be no logic in spring controller, but this is just an example):
#RestController
#RequestMapping("/api")
public class EntryPoint {
#Autowired
private ConversationService conversationService;
#Autowired
private UserDao userDao;
#PostMapping("/create/{userId}")
public ResponseEntity<String> createConversation(#PathVariable("userId") String userId) {
Optional<User> optionalUser = userDao.getById(userId);
if(optionalUser.isPresent()) {
User user = optionalUser.get();
Conversation conversation = conversationService.create(user, "default");
return ResponseEntity.ok(conversation.getId());
}
return ResponseEntity.notFound().build();
}
}
Interfaces ConversationService and UserDao have a generic type so warnings appear:
I don't want to give up generic types but on the other hand I'm aware that injecting without generic types will cause warnings which does not comply with clean code principles. It is true that this design will work despite warnings. I don't want to change implementation of the EntryPoint when I change persistence layer from MongoDb to JPA, I just want to provide new implementation for domain interfaces (User, UserDao, UserService etc.)
How to reconcile the interface issue for domain domain classes and injecting without generic type ?
This question already has answers here:
Update or saveorUpdate in CRUDRepository
(2 answers)
Closed 3 years ago.
I am processing a Post request with Json data, how do I add them to the database, so that there is a check that finds out if there is already this user in the database or not, if not, then create a new row, if there is, then update messages for this user.
What I accept in the request: phone, message
I do not quite understand how to implement it.
UserController
#RestController
#RequestMapping("/users")
public class UserController {
#Autowired
private UserRepo userRepo;
#PostMapping(consumes = {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE})
public String createUser(#Valid #RequestBody User requestUserDetails) {
userRepo.save(requestUserDetails);
return "The message delivered.";
}
}
User
#Entity
#Table(name = "ApiTable", schema = "TestApi")
public class User {
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
private Integer id;
private String phone;
private String message;
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getMessage() {
return message;
}
public void setLastName(String message) {
this.message = message;
}
}
UserRepo
public interface UserRepo extends CrudRepository<User, Long> {
}
Application
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
KeyUser
public class KeyUser implements Serializable {
private String phone;
private String message;
}
Application
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Well, the userRepo.save(requestUserDetails) already do it for you.
Look at the implementation of save method in SimpleJpaRepository<T, ID> class from Spring.
SimpleJpaRepository.java
#Transactional
public <S extends T> S save(S entity) {
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
org.springframework.data.jpa.repository.support.SimpleJpaRepository<T, ID> implements the JpaRepository<T, ID>, which is inherited by CrudRepository<T, ID>.
This already check if the object already exists on the database. All you have to supply is your entity with the primary key populated.
If the primary key is not populated, then Spring can't check if the object exists. So a new row will be created.
If the object already exists, it will call EntityManager.merge if not, it will call EntityManager.persist.
Take a look:
User use = new User();
user.setId(1);
user.setPhone("123");
user.setMessage("Hello");
userRepository.save(user);
In this case, if the User with id == 1 exists on the the database, it's information will be merged.
Now, if, for some reason, this don't fit for you, then you will have to do it manually, by searching for the user by it's ID and then applying some rule to it.
Something like this:
#RestController
#RequestMapping("/users")
public class UserController {
#Autowired
private UserRepo userRepo;
#PostMapping(consumes = {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE})
public String createUser(#Valid #RequestBody User requestUserDetails) {
User user = requestUserDetails;
if(requestUserDetails.getId() != null) {
Optional<User> userOpt = userRepo.findById(requestUserDetails.getId());
if (userOpt.isPresent()) {
user = userOpt.get();
// the user exists... do something..
}
}
// at the end, save the user anyway (event if it exists or not)
userRepo.save(user);
return "The message delivered.";
}
}
to update the object you can use the merge method. If the object has the id attribute, use merge, else, save.
I am trying to implement domain driven design in my project.
Here is my base Aggregate class:
public abstract class UUIDAggregate {
private final DomainEventPublisher domainEventPublisher;
protected void publish(DomainEvent domainEvent) {
domainEventPublisher.publish(domainEvent);
}
}
Let's say we have UserAccount aggregate:
public class UserAccount extends UUIDAggregate {
private String email;
private String username;
private String password;
private String firstName;
private String lastName;
public void update() {
publish(new DomainEventImpl());
}
}
Here is my DomainEventPublisher:
public interface DomainEventPublisher {
void publish(DomainEvent event);
}
Here is DomainEventPublisherImpl:
#Component
public class DomainEventPublisherImpl implements DomainEventPublisher{
#Autowired
private ApplicationEventPublisher publisher;
public void publish(DomainEvent event){
publisher.publishEvent(event);
}
}
Now, this seems like a good idea, the domain is separated from implementation but this does not work. DomainEventPublisher cannot be Autowired because UUIDAggregate is not a #Component or #Bean . One solution would be to create DomainService and publish event there but that seems like leaking of domain to domain service and if I go that way, I am going to anemic model. Also what I can do is to pass DomainEventPublisher as a parameter to every aggregate but that also does not seems like a good idea.
One idea would be to have a factory for domain objects:
#Component
class UserAccountFactoryImpl implements UserAccountFactory {
#Autowired
private DomainEventPublisher publisher;
#Override
public UserAccount newUserAccount(String email, String username, ...) {
return new UserAccount(email, username, ..., publisher);
}
}
Then your code creating a domain object is "publisher-free":
UserAccount userAccount = factory.newUserAccount("john#example.com", ...);
Or you might slightly change the design of the event-publishing:
public abstract class UUIDAggregate {
private final List<DomainEvent> domainEvents = new ArrayList<>();
protected void publish(DomainEvent domainEvent) {
domainEvents.add(domainEvent);
}
public List<DomainEvent> domainEvents() {
return Collections.unmodifiableList(domainEvents);
}
}
#Component
class UserAccountServiceImpl implements UserAccountService {
#Autowired
private DomainEventPublisher publisher;
#Override
public void updateUserAccount(UserAccount userAccount) {
userAccount.update();
userAccount.domainEvents().forEach(publisher::publishEvent);
}
}
This is different from your proposal: the service publishes the events, but doesn't create then - the logic stays in the domain object.
Further, you can change your publisher to minimize the boiler-plate code:
public interface DomainEventPublisher {
void publish(UUIDAggregate aggregate);
}
Vaughn Vernon in his book IDDD just uses singleton like this:
DomainEventPublisher.instance().register(...);
DomainEventPublisher.instance().publish(...);
I know this approach doesn't use spring injection but it's much simplier than passing publisher to every aggregate and not that hard to test.
this is my configuration
#EnableTransactionManagement
#EnableScheduling
#EnableAutoConfiguration
#ComponentScan(basePackages = {"id.co.babe.neo4j.service"})
#Configuration
public class MyNeo4jConfiguration extends Neo4jConfiguration {
private static final Logger LOGGER = LoggerFactory.getLogger(MyNeo4jConfiguration.class);
#Value("${neo4j.server.user}")
private String user;
#Value("${neo4j.server.pass}")
private String pass;
#Value("${neo4j.server.host}")
private String host;
#Override
public Neo4jServer neo4jServer() {
return new RemoteServer(host,user,pass);
}
#Override
public SessionFactory getSessionFactory() {
return new SessionFactory("app.neo4j.domain");
}
#Bean
#Primary
public Neo4jOperations getNeo4jTemplate() throws Exception {
return new Neo4jTemplate(getSession());
}
and this is my domain User
#NodeEntity
public class User{
#GraphId
private Long Id;
private String name;
private int age;
private String country;
and my service interface
public interface UserService {
public User create(User user);
public User read(User user);
public List<User> readAll();
public User update(User user);
public Boolean delete(User user);
}
and my implementation
#Service
#Transactional
public class UserServiceImpl implements UserService{
#Autowired
Neo4jOperations template;
#Override
public User create(User user){
return template.save(user);
}
and this is my main class
for(int i = 0; i < 10; i++){
app.neo4j.domain.User user = new app.neo4j.domain.User();
user.setAge(13);
user.setCountry("Philly");
user.setId(i);
user.setName("Ibanez" + i);
LOGGER.info("Inserting {}",user.getName());
service.create(user);
}
no error was found, but when I go to neo4j console (localhost:7474), and run this query match(n) return n, which should return all nodes in the database. unfortunately there was no nodes found even though i was able to save without errors. I wonder what's wrong.
I also tried doing it with #enablingNeo4jRepositories with no difference to the result.
Your code should never set the value of the #GraphId field. This field is used internally to attach entities to the graph.
If you remove user.setId(i);, your entities should be saved correctly.
Note that you can add your own custom ID field, but you still need another field for the GraphID e.g.
#GraphId private Long graphId; //used internally, never assign a value
private Long id; //your application id, stored as a property on the entity