I'm trying to understand SRP principle and most of the sof threads didn't answer this particular query I'm having,
Use-case
I'm trying to send an email to the user's email address to verify himself whenever he tries to register/create an user-account in a website.
Without SRP
class UserRegistrationRequest {
String name;
String emailId;
}
class UserService {
Email email;
boolean registerUser(UserRegistrationRequest req) {
//store req data in database
sendVerificationEmail(req);
return true;
}
//Assume UserService class also has other CRUD operation methods()
void sendVerificationEmail(UserRegistrationRequest req) {
email.setToAddress(req.getEmailId());
email.setContent("Hey User, this is your OTP + Random.newRandom(100000));
email.send();
}
}
The above class 'UserService' violates SRP rule as we are clubbing 'UserService' CRUD operations and triggering verification email code into 1 single class.
Hence I do,
With SRP
class UserService {
EmailService emailService;
boolean registerUser(UserRegistrationRequest req) {
//store req data in database
sendVerificationEmail(req);
return true;
}
//Assume UserService class also has other CRUD operation methods()
void sendVerificationEmail(UserRegistrationRequest req) {
emailService.sendVerificationEmail(req);
}
}
class EmailService {
void sendVerificationEmail(UserRegistrationRequest req) {
email.setToAddress(req.getEmailId());
email.setContent("Hey User, this is your OTP + Random.newRandom(100000));
email.send();
}
But even 'with SRP', UserService as a class again holds a behaviour of sendVerificationEmail(), though this time it didn't hold the entire logic of sending the email.
Isn't it again we are clubbing crud operation's and sendVerificationEmail() into 1 single class even after applying SRP?
Your feeling is absolutely right. I agree with you.
I think your problem starts with your naming style, since you seem to be quite aware what SRP means. Class names like '...Service' or '...Manager' carry a very vague meaning or semantics. They describe a more generalized context or concept. In other words a '...Manager' class invites you to put everything inside and it still feels right, because it's a manager.
When you get more concrete by trying to focus on the true concepts of your classes or their responsibilities, you will automatically find bigger names with a stronger meaning or semantics. This will really help you to split up classes and to identify responsibilities.
SRP:
There should never be more than one reason to change a certain module.
You could start with renaming the UserService to UserDatabaseContext. Now this would automatically force you to only put database related operations into this class (e.g. CRUD operations).
You even can get more specific here. What are you doing with a database? You read from and write to it. Obviously two responsibilities, which means two classes: one for read operations and another responsible for write operations. This could be very general classes that can just read or write anything. Let's call them DatabaseReader and DatabaseWriter and since we are trying to decouple everything we are going to use interfaces everywhere. This way we get the two IDatabaseReader and IDatabaseWriter interfaces. This types are very low level since they know the database (Microsoft SQL or MySql), how to connect to it and the exact language to query it (using e.g. SQL or MySql):
// Knows how to connect to the database
interface IDatabaseWriter {
void create(Query query);
void insert(Query query);
...
}
// Knows how to connect to the database
interface IDatabaseReader {
QueryResult readTable(string tableName);
QueryResult read(Query query);
...
}
On top, you could implement a more specialized layer of read and write operations, e.g. user related data. We would introduce a IUserDatabaseReader and a IUserDatabaseWriter interface. This interfaces don't know how to connect to the database or what type of database is used. This interfaces only know what information is required to read or write user details (e.g. using a Query object that is transformed into a real query by the low level IDatabaseReader or IDatabaseWriter):
// Knows only about structure of the database (e.g. there is a table called 'user')
// Implementation will internally use IDatabaseWriter to access the database
interface IUserDatabaseWriter {
void createUser(User newUser);
void updateUser(User user);
void updateUserEmail(long userKey, Email emailInfo);
void updateUserCredentials(long userKey, Credential userCredentials);
...
}
// Knows only about structure of the database (e.g. there is a table called 'user')
// Implementation will internally use IDatabaseReader to access the database
interface IUserDatabaseReader {
User readUser(long userKey);
User readUser(string userName);
Email readUserEmail(string userName);
Credential readUserCredentials(long userKey);
...
}
We are still not done with the persistence layer. We can introduce another interface IUserProvider. The idea is to decouple the database access from the rest of our application. In other words we consolidate the user related data query operations into this class. So, IUserProvider will be the only type that has direct access to the data layer. It forms the interface to the application's persistence layer:
interface IUserProvider {
User getUser(string userName);
void saveUser(User user);
User createUser(string userName, Email email);
Email getUserEmail(string userName);
}
The implementation of IUserProvider. The only class in the whole application that has direct access to the data layer by referencing IUserDatabaseReader and IUserDatabaseWriter. It wraps reading and writing of data to make data handling more convenient. The responsibility of this type is to provide user data to the application:
class UserProvider {
IUserDatabaseReader userReader;
IUserDatabaseWriter userWriter;
// Constructor
public UserProvider (IUserDatabaseReader userReader,
IUserDatabaseWriter userWriter) {
this.userReader = userReader;
this.userWriter = userWriter;
}
public User getUser(string userName) {
return this.userReader.readUser(username);
}
public void saveUser(User user) {
return this.userWriter.updateUser(user);
}
public User createUser(string userName, Email email) {
User newUser = new User(userName, email);
this.userWriter.createUser(newUser);
return newUser;
}
public Email getUserEmail(string userName) {
return this.userReader.readUserEmail(userName);
}
}
Now that we tackled the database operations we can focus on the authentication process and continue to extract the authentication logic from the UserService by adding a new interface IAuthentication:
interface IAuthentication {
void logIn(User user)
void logOut(User);
void registerUser(UserRegistrationRequest registrationData);
}
The implementation of IAuthentication implements the special authentication procedure:
class EmailAuthentication implements IAuthentication {
EmailService emailService;
IUserProvider userProvider;
// Constructor
public EmailAuthentication (IUserProvider userProvider,
EmailService emailService) {
this.userProvider = userProvider;
this.emailService = emailService;
}
public void logIn(string userName) {
Email userEmail = this.userProvider.getUserEmail(userName);
this.emailService.sendVerificationEmail(userEmail);
}
public void logOut(User user) {
// logout
}
public void registerUser(UserRegistrationRequest registrationData) {
this.userProvider.createNewUser(registrationData.getUserName, registrationData.getEmail());
this.emailService.sendVerificationEmail(registrationData.getEmail());
}
}
To decouple the EmailService from the EmailAuthentication class, we can remove the dependency on UserRegistrationRequest by letting sendVerificationEmail() take an Email` parameter object instead:
class EmailService {
void sendVerificationEmail(Email userEmail) {
email.setToAddress(userEmail.getEmailId());
email.setContent("Hey User, this is your OTP + Random.newRandom(100000));
email.send();
}
Since the authentication is defined by an interface IAuthentication, you can create a new implementation at any time when you decide to use a different procedure (e.g. WindowsAuthentication), but without modifying existing code. This will also work with the IDatabaseReader and IDatabaseWriter once you decide to switch to a different database (e.g. Sqlite). The IUserDatabaseReader and IUserDatabaseWriter implementations will still work without any modification.
With this class design, you now have exactly one reason to modify each existing type:
EmailService when you need to change the implementation (e.g. use
different email API)
IUserDatabaseReader or IUserDatabaseWriter when you want to add additional user related read or write operations (e.g. to handle user role)
provide new implementations of IDatabaseReader or IDatabaseWriter when you want to switch underlying database or you need to modify database access
implementations of IAuthentication when the procedure changes (e.g. using build in OS authentication)
Now everything is cleanly separated. Authentication doesn't mix with CRUD operations. We have an additional layer between application and persistence layer to add flexibility regarding the underlying persistence system. So CRUD operations don't mix with the actual persistence operations.
As a tip: in future you better start with the thinking (design) part first: what must my application do?
handle authentication
handle users
handle a database
handle email
create user responses
show view pages to the user
etc.
As you can see, you can start to implement each step or requirement separately. But this doesn't mean each requirement is realized by exactly one class. As you remember, we split up database access into four responsibilities or classes: read and write to real database (low level), read and write to database abstraction layer, to reflect concrete use cases (high level). Using interfaces adds flexibility and testability to the application.
There is already a great answer to this question by #BionicCode. I just wan't to add a short summary and some of my thoughts on the matter.
The SRP can be a tricky one.
In my experience the granularity of the responsibilities and the number of abstactions that you place in your system will affect it's ease of use and it's size.
You can add a-lot of abstractions and break everything down to very small components. This indeed is something that we should strive for.
Now the question then is: When to stop?
This will depend on:
The size of your application
What parts of it will change more frequently than others
Do you need to compose objects together, or most of the time your modules are independent of one another and you don't reause many objects.
What time do you have
What is the size of your team
A lot of other stuff...
Let's start with how big is the team.
One reason we break our code into separate modules and classes into seprate files is so that we can work in a team and avoid too many merges in our favorite source control system. If you need to change a file that contains a component of your system and someone else needs to change it too, this may get ugly pretty fast. Now if you do separate modules using SRP you get more but smaller modules that most of the time will change independent of one another.
What if the team isn't that big and our modules are not that big too? Do you need to generate more of them?
Here's an example.
Let's say that you have a mobile application that has setings. We may say that containg these settigns in one responsibility and add it to one interface IApplicationSettings to hold all of them.
In the case where we have 30 settings this interface will be huge and that's bad. It also means that we are probably violating the SRP again as this interface will probably hold settings for multiple different categories.
So we decide to apply Interface seggregation principle and SRP and divide the settings to multiple interfaces ISomeCategorySettings, IAnotherCategorySettings etc.
Now let's say that our applications isn't too big (yet) and we have 5 settings. Even if they are from different categories, is it bad to keep these settings in one interface?
I would say that it's fine to have all settigns in one interface as long as it doesn't start to slow us down or start to get ugly (30 or more settigns!).
Is it that bad to construct an email and send it from your service object? This indeed is something that can get ugly pretty quickly, so you better move this responsibility from the service object to an EmailSender fast.
If you have a service object that contains 5 methods, do you realy need to break this into 5 different objects for every operation? If these methods are big, yes. If they small, keeping them in one object it's that big of a problem.
SRP is great, but take granularity into account and choose it wisely based on code size, team size etc.
Related
I'm developing a SpringBoot web application for managing gaming servers.
I want to have a cronjob that queries the servers, checks whether they have crashed and collects relevant data, such as the number of players online etc. This data needs to be stored and shared among services that require it. Since this data will change often and will become invalid after the whole application stops, I don't want to persist these stats in the database, but in the application memory.
Current implementation
Currently, my implementation is pretty naive - having a collection as a member field of the corresponding Spring service and storing the server statuses there. However I feel this is a really bad solution, as the services should be stateless and also I don't take concurrency into account.
Example code:
#Service
public class ServersServiceImpl implements ServersService {
private final Map<Long, ServerStats> stats = new HashMap<>(); // Map server ID -> stats
...
public void startServer(Long id) {
// ... call service to actually start server process
serverStats.setRunning(true);
stats.put(id, serverStats);
}
...
}
Alternative: Using #Repository classes
I could move the collection with the data to classes with #Repository annotation, which would be semantically more correct. There, I would implement a thread-safe logic of storing the data in java collection. Then I would inject this repository into relevant services.
#Repository
public class ServerStatsRepository {
private final Map<Long, ServerStats> stats = new ConcurrentHashMap<>();
...
public ServerStats getServerStats(Long id) {
return stats.get(id);
}
public ServerStats updateServerStats(Long id, ServerStats serverStats) {
return stats.put(id, serverStats);
}
...
}
Using Redis also came to mind, but I don't want to add too much complexity to the app.
Is my proposed solution a valid approach? Would there be any better option of handling this problem?
I've read that accessing repository from aggregate root considered bad practice.
If it is, than consider following example:
class User {
private String username;
public void changeUsername(String newUsrname) {
// How will I persist username to database if I don't have access to repository from aggregate root?
...
}
}
How will I persist username to database if I don't have access to repository
from aggregate root?
I see it like this:
class User {
private String username;
private UserRepository userRepository;
public User(UserRepository userRepository) {
this.userRepository = userRepository;
}
public void changeUserName(String newUsername) {
this.username = newUserName;
userRepository.save(this);
}
}
Or I've missed something in DDD concepts?
How will I persist username to database if I don't have access to repository from aggregate root?
Current practices normally handle I/O in the application component, rather than in the domain model.
Application {
void when(ChangeUserName command) {
User user = this.userRepository.getUserById(command.userId);
user.changeName(command.name);
this.userRepository.save(user);
}
}
Recommended reading: Vladimir Khorikov on domain model isolation.
Just to extend a little the answer given by #VoiceOfUnreason (which is totally right) here a quick explanation on why injecting repositories via constructor to Aggregate Roots is not advisable:
The repository should depend on the object it returns, not the other way around. The reason for this is that your "domain object" (more on that later) can exist (and should be testable) without being loaded or saved (that is, having a dependency on a repository).
Basically your design says that in order to have an user, you need to provide a MySQL/Mongo/XXX instance connection which is an infrastructure detail. Your domain should not know anything about how it is persisted. Your domain knows about the behavior, invariants, business rules and so on.
These concepts just help you to create code easier to maintain as well as help you to apply best practices such as SRP (Single Responsibility Principle).
It's RESTful web app. I am using Hibernate Envers to store historical data. Along with revision number and timestamp, I also need to store other details (for example: IP address and authenticated user). Envers provides multiple ways to have a custom revision entity which is awesome. I am facing problem in setting the custom data on the revision entity.
#RevisionEntity( MyCustomRevisionListener.class )
public class MyCustomRevisionEntity extends DefaultRevisionEntity {
private String userName;
private String ip;
//Accessors
}
public class MyCustomRevisionListener implements RevisionListener {
public void newRevision( Object revisionEntity ) {
MyCustomRevisionEntity customRevisionEntity = ( MyCustomRevisionEntity ) revisionEntity;
//Here I need userName and Ip address passed as arguments somehow, so that I can set them on the revision entity.
}
}
Since newRevision() method does not allow any additional arguments, I can not pass my custom data (like username and ip) to it. How can I do that?
Envers also provides another approach as:
An alternative method to using the org.hibernate.envers.RevisionListener is to instead call the getCurrentRevision( Class revisionEntityClass, boolean persist ) method of the org.hibernate.envers.AuditReader interface to obtain the current revision, and fill it with desired information.
So using the above approach, I'll have to do something like this:
Change my current dao method like:
public void persist(SomeEntity entity) {
...
entityManager.persist(entity);
...
}
to
public void persist(SomeEntity entity, String userName, String ip) {
...
//Do the intended work
entityManager.persist(entity);
//Do the additional work
AuditReader reader = AuditReaderFactory.get(entityManager)
MyCustomRevisionEntity revision = reader.getCurrentRevision(MyCustomRevisionEntity, false);
revision.setUserName(userName);
revision.setIp(ip);
}
I don't feel very comfortable with this approach as keeping audit data seems a cross cutting concern to me. And I obtain the userName and Ip and other data through HTTP request object. So all that data will need to flow down right from entry point of application (controller) to the lowest layer (dao layer).
Is there any other way in which I can achieve this? I am using Spring.
I am imagining something like Spring keeping information about the 'stack' to which a particular method invocation belongs. So that when newRevision() in invoked, I know which particular invocation at the entry point lead to this invocation. And also, I can somehow obtain the arguments passed to first method of the call stack.
One good way to do this would be to leverage a ThreadLocal variable.
As an example, Spring Security has a filter that initializes a thread local variable stored in SecurityContextHolder and then you can access this data from that specific thread simply by doing something like:
SecurityContext ctx = SecurityContextHolder.getSecurityContext();
Authorization authorization = ctx.getAuthorization();
So imagine an additional interceptor that your web framework calls that either adds additional information to the spring security context, perhaps in a custom user details object if using spring security or create your own holder & context object to hold the information the listener needs.
Then it becomes a simple:
public class MyRevisionEntityListener implements RevisionListener {
#Override
public void newRevision(Object revisionEntity) {
// If you use spring security, you could use SpringSecurityContextHolder.
final UserContext userContext = UserContextHolder.getUserContext();
MyRevisionEntity mre = MyRevisionEntity.class.cast( revisionEntity );
mre.setIpAddress( userContext.getIpAddress() );
mre.setUserName( userContext.getUserName() );
}
}
This feels like the cleanest approach to me.
It is worth noting that the other API getCurrentRevision(Session,boolean) was deprecated as of Hibernate 5.2 and is scheduled for removal in 6.0. While an alternative means may be introduced, the intended way to perform this type of logic is using a RevisionListener.
I have written some code which I thought was quite well-designed, but then I started writing unit tests for it and stopped being so sure.
It turned out that in order to write some reasonable unit tests, I need to change some of my variables access modifiers from private to default, i.e. expose them (only within a package, but still...).
Here is some rough overview of my code in question. There is supposed to be some sort of address validation framework, that enables address validation by different means, e.g. validate them by some external webservice or by data in DB, or by any other source. So I have a notion of Module, which is just this: a separate way to validate addresses. I have an interface:
interface Module {
public void init(InitParams params);
public ValidationResponse validate(Address address);
}
There is some sort of factory, that based on a request or session state chooses a proper module:
class ModuleFactory {
Module selectModule(HttpRequest request) {
Module module = chooseModule(request);// analyze request and choose a module
module.init(createInitParams(request)); // init module
return module;
}
}
And then, I have written a Module that uses some external webservice for validation, and implemented it like that:
WebServiceModule {
private WebServiceFacade webservice;
public void init(InitParams params) {
webservice = new WebServiceFacade(createParamsForFacade(params));
}
public ValidationResponse validate(Address address) {
WebService wsResponse = webservice.validate(address);
ValidationResponse reponse = proccessWsResponse(wsResponse);
return response;
}
}
So basically I have this WebServiceFacade which is a wrapper over external web service, and my module calls this facade, processes its response and returns some framework-standard response.
I want to test if WebServiceModule processes reponses from external web service correctly. Obviously, I can't call real web service in unit tests, so I'm mocking it. But then again, in order for the module to use my mocked web service, the field webservice must be accessible from the outside. It breaks my design and I wonder if there is anything I could do about it. Obviously, the facade cannot be passed in init parameters, because ModuleFactory does not and should not know that it is needed.
I have read that dependency injection might be the answer to such problems, but I can't see how? I have not used any DI frameworks before, like Guice, so I don't know if it could be easily used in this situation. But maybe it could?
Or maybe I should just change my design?
Or screw it and make this unfortunate field package private (but leaving a sad comment like // default visibility to allow testing (oh well...) doesn't feel right)?
Bah! While I was writing this, it occurred to me, that I could create a WebServiceProcessor which takes a WebServiceFacade as a constructor argument and then test just the WebServiceProcessor. This would be one of the solutions to my problem. What do you think about it? I have one problem with that, because then my WebServiceModule would be sort of useless, just delegating all its work to another components, I would say: one layer of abstraction too far.
Yes, your design is wrong. You should do dependency injection instead of new ... inside your class (which is also called "hardcoded dependency"). Inability to easily write a test is a perfect indicator of a wrong design (read about "Listen to your tests" paradigm in Growing Object-Oriented Software Guided by Tests).
BTW, using reflection or dependency breaking framework like PowerMock is a very bad practice in this case and should be your last resort.
I agree with what yegor256 said and would like to suggest that the reason why you ended up in this situation is that you have assigned multiple responsibilities to your modules: creation and validation. This goes against the Single responsibility principle and effectively limits your ability to test creation separately from validation.
Consider constraining the responsibility of your "modules" to creation alone. When they only have this responsibility, the naming can be improved as well:
interface ValidatorFactory {
public Validator createValidator(InitParams params);
}
The validation interface becomes separate:
interface Validator {
public ValidationResponse validate(Address address);
}
You can then start by implementing the factory:
class WebServiceValidatorFactory implements ValidatorFactory {
public Validator createValidator(InitParams params) {
return new WebServiceValidator(new ProdWebServiceFacade(createParamsForFacade(params)));
}
}
This factory code becomes hard to unit-test, since it is explicitly referencing prod code, so keep this impl very concise. Put any logic (like createParamsForFacade) on the side, so that you can test it separately.
The web service validator itself only gets the responsibility of validation, and takes in the façade as a dependency, following the Inversion of Control (IoC) principle:
class WebServiceValidator implements Validator {
private final WebServiceFacade facade;
public WebServiceValidator(WebServiceFacade facade) {
this.facade = facade;
}
public ValidationResponse validate(Address address) {
WebService wsResponse = webservice.validate(address);
ValidationResponse reponse = proccessWsResponse(wsResponse);
return response;
}
}
Since WebServiceValidator is not controlling the creation of its dependencies anymore, testing becomes a breeze:
#Test
public void aTest() {
WebServiceValidator validator = new WebServiceValidator(new MockWebServiceFacade());
...
}
This way you have effectively inverted the control of the creation of the dependencies: Inversion of Control (IoC)!
Oh, and by the way, write your tests first. This way you will naturally gravitate towards a testable solution, which is usually also the best design. I think that this is due to the fact that testing requires modularity, and modularity is coincidentally the hallmark of good design.
I am developing a Web App in Spring and hibernate.
I am loading entities in Database.Authors,books,Publication etc are my entities which are getting loaded from excel.
I have mode one Entity Load Service interface and then I have its Implementations for every entity.
My Service calls DAO implementations.
Now I am struggling to find if the below mentioned code violates SRP.
Also I am always confused about how to decide responsibility of the class because any class can have many methods and each method can be performing something different.So should they be separated in different class?.take in my case I have 4 methods each performing different task.So I end up with 4 different class for each method.If I follow this approach(which I know is wrong) then I will always end up in classes having single method.
Also,sometimes I feel that I going away from domain driven design because I am refracting the code on the basis of functionality.
Any suggestions on how to decide what the responsibility is from the perspective a class?
SRP stands for single responsibility principle.And I am really confused in identifying this responsibility.
public interface EntitiesLoadService {
public void loadEntities(Object o);
public void deleteEntities(Object o);
public List getEntities();
public Object getEntity(Object o);
}
Service Implementation
#Service("authorLoadService")
#Transactional
public class AuthorEntityLoadService implements EntitiesLoadService{
private AuthorDAO authorDao;
#Autowired
#Qualifier("authorDAO")
public void setAuthorDao(AuthorDAO authorDao) {
this.authorDao = authorDao;
}
#Override
public void deleteEntities(Object o) {
// TODO Auto-generated method stub
}
#Override
public void loadEntities(Object o) {
Set<author_pojo> author=(Set<author_pojo>)o;
Iterator<author_pojo> itr=author.iterator();
while (itr.hasNext()) {
author_pojo authorPojo = (author_pojo) itr.next();
authorDao.save(authorPojo);
}
}
#Override
#Transactional(readOnly=true)
public List getEntities() {
// TODO Auto-generated method stub
return null;
}
#Override
#Transactional(readOnly=true)
public Object getEntity(Object o) {
String author=(String)o;
author_pojo fetAuthor=authorDao.findOneByName(author);
return fetAuthor;
}
}
You have AuthorDAO which is the class that should be doing all interactions with the persistence layer, ex. a database.
It isn't obvious in your example because your AuthorEntityLoadService has similar methods which just delegate to the DAO layer.
As your project and requirements grow, you will see that more methods are required for this class. These methods will be responsible for doing more than just CRUD operations on the DAO layer. They might need to interact with other services, internal or external. They might need to do multiple DAO calls.
The Single Responsibility in this case is to provide services for interacting with AuthorEntity instances.
It is on of many correct ways of implementing what you are proposing.
More specifically, my opinion on
Also I am always confused about how to decide responsibility of the
class because any class can have many methods and each method can be
performing something different.So should they be separated in
different class?
Just because you have many methods doing different things, doesn't mean the responsibility isn't shared. AuthorEntityLoadService which I would just call AuthorEntityService manages AuthorEntity instances at the service layer. Image if you had one Class with one method for each of create, update, retrieve, delete an AuthorEntity. That wouldn't make much sense.
And on
Any suggestions on how to decide what the responsibility is from the
perspective a class?
As further reading, try http://java.dzone.com/articles/defining-class-responsibility
Typically, in this type of n-tier architecture, your service layer is meant to provide an API of transactional (or otherwise resource-dependent) operations. The implementation of each service can use whatever resource-specific dependencies (like DAOs for a particular datasource) it needs, but it allows the service consumer to remain agnostic of these specific dependencies or resources.
So even if your service is just delegating to its resource-specific dependencies, it doesn't violate SRP because its responsibility is to define a resource-agnostic API (so that the consumer doesn't need to know all the resource-specific stuff) that specifies atomic operations (transactional if necessary).