Are you interested in automated testing? If so, become a member of the Java Testing Society!

The Biggest Flaw of Spring Web Applications

Iceberg

The developers who are using Spring Framework in their applications are good to talk about the benefits of dependency injection.

Unfortunately, they are not so good to leverage its benefits such as the single responsible principle and separation of concerns in their applications.

If we take a look at any Spring powered web application, the odds are that the application is implemented by using these common and equally erroneous design principles:

  1. The domain model objects are used only to store the data of the application. In other words, the domain model follows the anemic domain model anti-pattern.
  2. The business logic lies in the service layer which manages the data of the domain objects.
  3. The service layer has one service class per each entity of the application.

The question is:

If this is so common, how can it be wrong?

Let’s find out.

Old Habits Die Hard

The reason why Spring web applications look this way is that this is the way things have always been done, and old habits die hard, especially if they are enforced by senior developers or software architects.

The problem is that these people are very good at defending their opinions. One of their favourite arguments is that our application follows the separation of concerns principle because it has been divided into several layers and each layer has specific responsibilities.

A typical Spring web application has the following layers:

  • The web layer which is responsible of processing user’s input and returning the correct response back to the user. The web layer communicates only with the service layer.
  • The service layer which acts as a transaction boundary. It is also responsible of authorization and contains the business logic of our application. The service layer manages the domain model objects and communicates with other services and the repository layer.
  • The repository / data access layer which is responsible of communicating with the used data storage.

The separation of concerns principle is defined as follows:

Separation of concerns (Soc) is a design principle for separation a computer program into distinct sections, such that each section addresses a separate concern.

Although it is true that a typical Spring web application follows this principle in some level, the reality is that the application has a monolithic service layer which has too many responsibilities.

To be more specific, the service layer has two major problems:

First, the business logic of the application is found from the the service layer.

This is a problem because the business logic is scattered around the service layer. If we need to check how a certain business rule is implemented, we have to find it first. This might not be easy.

Also, if the same business rule is needed in multiple service classes, the odds are that the rule is simply copied from one service to another. This leads into a maintenance nightmare.

Second, the service layer has one service class per each domain model class.

This violates the single responsibility principle which is defined as follows:

The single responsibility principle states that every class should have a single responsibility, and that responsibility should be entirely encapsulated by the class. All its services should be narrowly aligned with that responsibility.

The service classes have a lot of dependencies and a lot of circular dependencies. The service layer of a typical Spring web application does not consist of loosely coupled services which have only one responsibility. It is a more like a net of tightly coupled and monolithic services.

This makes is it hard to understand, maintain and reuse.

This might sound a bit harsh but the service layer is often the most problematic part of a Spring web application.

Luckily for us, all hope is not lost.

Breaking Free

The current situation is bad, but it is not totally hopeless. Let’s find out how we can break free from old habits.

First, we have to move the business logic of our application from the service layer to the domain model classes.

The reason why this make sense should be clear to us if we think of the following example:

Let’s assume that I am a service class and you are a domain model object. If a tell you to jump off from a roof, would you prefer to have a veto right to my decision?

Moving the business logic from the service layer to the domain model classes gives us three advantages:

  1. The responsibilities of our code are divided in a logical way. The service layer takes care of the application logic and our domain model classes takes care of the business logic.
  2. The business logic of our application is found from a single place. If we need to verify how a specific business rule is implemented, we always know where to look for.
  3. The source code of the service layer is cleaner and does not contain any copy paste code.

Second, we have to divide the entity specific services into smaller services which serves only a single purpose.

For example, if our application has a single service class which provides CRUD operations for persons and operations related to user accounts, we should divide it into two separate service classes:

  • The first service provides CRUD operations for persons.
  • The second service provides operations related to user accounts.

This gives us three big advantages:

  1. Each service class has a logical set of responsibilities.
  2. Each service class has less dependencies which means that they are no longer tightly coupled giants. They are smaller and loosely coupled components.
  3. The service classes are easier to understand, maintain and reuse.

These two simple steps will help us to clean up the architecture of our application, and increase the productivity and happiness of our fellow developers.

Now, we might be wondering if all this is really necessary and if so, when it is critical to address these issues?

Sometimes Life Is Black and White

I have often heard an argument which states that we should not pay much attention to the “architecture” because our application is small and simple. Although this argument has some truth in it, we must remember that a project which starts small can grow into something much bigger.

If we don’t take this into account when it happens, we are screwed.

Sailing in uncharted waters might sound like a bad idea but we must remember that Titanic was sailing in a familiar route when it was hit by an iceberg which sank it. This same thing might be happening to our application right now.

We must have the courage to yell STOP when things are getting out of control.

P.S. If you are ready to take the red pill, I recommend that you read Whoops! Where did my architecture go by Olivier Gierke (or watch his SpringOne2GX presentation about the same subject). But beware, the rabbit hole goes much deeper than you think.

About the Author

Petri Kainulainen is passionate about software development and continuous improvement. He is specialized in software development with the Spring Framework and is the author of Spring Data book.

About Petri Kainulainen →

31 comments… add one

  • You’re spot on. Can I propose book: Domain-driven Design by Eric Evans. It brings Domain back to software development. ORM killed proper domains – Let’s not blame Spring completely.. :)

    Reply
    • I have actually read the Domain-driven design. It is a great book and I think that every software developer should read it.

      Also, I agree that the ORM frameworks started the anemic domain model “movement”.

      The reason why I decided to write a rant about Spring web applications is that most Spring applications, which I have seen, seem to suffer from this particular problem.

      But like you said, this problem is not related to any specific framework. It seems to be more like an ideology. A bad one.

      Reply
  • The flaws you are mentioning can more often than not be removed by simple refactoring. When it comes to complex business rules, they often involve backend validation, which IMHO shouldn’t reside in the domain model. So, the amount of business logic one can move to the domain model is sometimes limited. In addition, a database constraint is usually more performant than to load a complete aggregate in order to code the business rule in Java.

    No, the biggest flaw I almost always find in all sorts of applications is not to abstract simple attributes. E.g., the attribute lastName should be of the class LastName and not String. Even if String is used internally in that class, that’s still an implementation detail.

    When the attribute is exposed as a plain string, everyone suspects, that it could be corrupted and adds some validation code. Getting rid of that code is really cumbersome.

    I found JPA to be a huge step backwards with respect to user-defined types, because Hibernate, TopLink, EclipseLink etc. offered them for a long time already. With Spring’s conversion framework it is a no-brainer to make use of user-defined types in the web layer, too.

    Reply
    • First, I want to thank you for your comment. It is quite interesting.

      “The flaws you are mentioning can more often than not be removed by simple refactoring.”

      In theory that is the case. However, when the application is big enough, the service layer is typically an utter mess. That makes the refactoring process a bit more complex. The principles are of course the same but it just requires much more effort and time.

      “When it comes to complex business rules, they often involve backend validation, which IMHO shouldn’t reside in the domain model.”

      If the validation checks that the data entered by the user “correct”, it should be done in the web layer. However, if the validation is used to enforce a business rule, it should be added to the domain model. One thing which we should remember is that we cannot (and should not) validate everything in the same place.

      Here are some additional resources which should shed some light to this topic:

      “In addition, a database constraint is usually more performant than to load a complete aggregate in order to code the business rule in Java.”

      Database constraints should be used only to verify that

      1. Only valid data can be added to the database.
      2. The integrity of the data is not compromised.

      Database is just a data storage and it should be treated as such. Enforcing business rules in a database is a horrible idea for three reasons:

      1. It is just a database. Its responsibility is store data and serve it back to the application.
      2. We want to keep the business logic in a single place.
      3. Writing tests for logic stored to a database is cumbersome.

      Note: There are always exceptions but the general guideline is that business logic does not belong to a database.

      “No, the biggest flaw I almost always find in all sorts of applications is not to abstract simple attributes. E.g., the attribute lastName should be of the class LastName and not String. Even if String is used internally in that class, that’s still an implementation detail.”

      This is an interesting topic and there are valid arguments for using custom types but we must also remember that there is a fine line between reason and insanity. What I am trying to say is that we have to think very carefully when using custom types makes sense. Sometimes using a String instead of a simple wrapper class makes perfect sense.

      “I found JPA to be a huge step backwards with respect to user-defined types, because Hibernate, TopLink, EclipseLink etc. offered them for a long time already. With Spring’s conversion framework it is a no-brainer to make use of user-defined types in the web layer, too.”

      You are spot on!

      Reply
  • “Also, if the same business rule is needed in multiple service classes, the odds are that the rule is simply copied from one service to another.”

    If your services don’t call each other and things are copied instead of called… You can’t even start to talk about design patterns.

    Reply
    • I agree!

      I was not probably clear enough but I did not mean that entire service methods would be copied from one class to another. I was talking about those innocent looking if clauses which are often used to enforce business rules in the service layer.

      I will use an example to demonstrate what I meant.

      Let’s assume that we have to write a simple application which can be used to withdraw money from a bank account and transfer money to another bank account (sorry, could not figure out a better example. Sigh).

      “Surprisingly”, the name of our domain model class is BankAccount and its source code looks as follows:

      
      public class BankAccount {
      
      	private double balance;
      
      	private String iban;
      
      	public void deposit(double amount) {
      		balance += amount;
      	}
      
      	public double getBalance() {
      		return balance;
      	}
      }
      
      

      Following the bad practices

      Now, first I will write a service implementation which follows the “bad practices” which I mentioned in the original blog post. It source code looks as follows:

      
      public class BankAccountServiceImpl implements BankAccountService {
      
      	public void withdraw(String iban, double amount) 
      		throws InsufficientBalanceException {
      		
      		BankAccount account = //Find bank account by using iban
      		
      		//Tiny innocent if clause which can be copied from one class to another
      		if (account.getBalance() < amount) {
      			throw new InsufficientBalanceException();
      		}
      		
      		account.withdraw(amount);
      	}
      	
      	public void transfer(String fromIban, String toIban, double amount) 
      		throws InsufficientBalanceException {
      		
      		BankAccount from = //Find bank account by using fromIban.
      		BankAccount to = //Find bank account by using toIban.
      		
      		//Tiny innocent if clause which can be copied from one class to another
      		if (from.getBalance() < amount) {
      			throw new InsufficientBalanceException();
      		}
      		
      		from.transfer(to, amount);
      	}
      }
      
      

      We need to also add the withdraw() and transfer() methods to the BankAccount class. Its source code looks as follows:

      
      public class BankAccount {
      
      	private double balance;
      
      	private String iban;
      
      	public void deposit(double amount) {
      		balance += amount;
      	}
      
      	public double getBalance() {
      		return balance;
      	}
      	
      	public void transfer(BankAccount target, double amount) {
      		this.withdraw(amount);
      		target.deposit(amount);
      	}
      	
      	public void withdraw(double amount) {
      		balance -= amount;
      	}
      }
      
      

      The first thing which often happens is that we notice that we have duplicate code on our service class and move it to a private method. Let's call this method hasSufficientBalance(). After we done this, the source code of our service layer looks as follows:

      
      public class BankAccountServiceImpl implements BankAccountService {
      
      	public void withdraw(String iban, double amount) 
      		throws InsufficientBalanceException {
      		
      		BankAccount account = //Find bank account by using iban		
      		hasSufficientFunds(account, amount);
      		account.withdraw(amount);
      	}
      	
      	public void transfer(String fromIban, String toIban, double amount) 
      		throws InsufficientBalanceException {
      		
      		BankAccount from = //Find bank account by using fromIban.
      		BankAccount to = //Find bank account by using toIban.
      		
      		hasSufficientFunds(from, amount);		
      		from.transfer(to, amount);
      	}
      	
      	//What if we need this in another class? 
      	private void hasSufficientFunds(BankAccount account, double requiredBalance) 
      		throws InsufficientBalanceException {
      		
      		if (account.getBalance() < requiredBalance) {
      			throw new InsufficientBalanceException();
      		}
      	}
      }
      
      

      This is a bit better but at this point we should really question if the service layer is the right place for this code. I think that the code which verifies that a bank account has sufficient balance does not belong to the service layer because it is used to enforce a business rule.

      Let's move on and find out what we can do about it.

      Moving business logic to the domain model class

      After careful consideration we decided that we should move the code, which verifies that a bank account has sufficient funds, to our domain model class. Its source code looks as follows:

      
      public class BankAccount {
      
      	private double balance;
      
      	private String iban;
      
      	public void deposit(double amount) {
      		balance += amount;
      	}
      
      	public double getBalance() {
      		return balance;
      	}
      	
      	public void transfer(BankAccount target, double amount) 
      		throws InsufficientBalanceException {
      		
      		this.withdraw(amount);
      		target.deposit(amount);
      	}
      	
      	public void withdraw(double amount)  throws InsufficientBalanceException {
      		if (balance < amount) {
      			throw new InsufficientBalanceException();
      		}
      		
      		balance -= amount;
      	}
      }
      
      

      Now we can clean up our service class by removing the unnecessary code from it. After we have done it, the source code of our service class looks as follows:

      
      public class BankAccountServiceImpl implements BankAccountService {
      
      	public void withdraw(String iban, double amount) 
      		throws InsufficientBalanceException {
      		
      		BankAccount account = //Find bank account by using iban		
      		account.withdraw(amount);
      	}
      	
      	public void transfer(String fromIban, String toIban, double amount) 
      		throws InsufficientBalanceException {
      		
      		BankAccount from = //Find bank account by using fromIban.
      		BankAccount to = //Find bank account by using toIban.
      	
      		from.transfer(to, amount);
      	}
      }
      
      

      We are done. The source code of our service class is much cleaner. However, that is only a side effect. What is more important is that the responsibilities of our code are divided in a logical way.

      P.S. I left a small defect to the BankAccount class and one question to the BankAccountServiceImpl class. If you find the defect or have an answer to my question, leave a comment to this blog post. I would love to hear your opinion!

      Reply
  • I have a question. What about data access when implementing business logic in Domain Model?

    Suppose I have a complex object graph. For example, an Order can have many Items which can be related to Shipments which have “finished” flag. An Order is deletable only if all of its Items’ Shipments are finished. It would be easy to implement order.isDeletable() method if we had immediate access to all of its items which in their turn would have immediate access to shipments. But there is a problem if we need to access database first.

    How do you achieve that? Would you fetch a big object graph at once (a special repository method to get order with items and with their shipments??)? Or use lazy fetching enabled by Hibernate or EclipseLink (possibly running into the problems of suboptimal queries???)?

    Reply
    • Thank your for an excellent question!

      Here is what I would do:

      1. I would start by implementing the simplest thing that could possibly work and avoid premature optimization. This means that I would add the business logic to the domain model class and use lazy fetching. Now, if the performance of this approach becomes a problem and I can prove it, I would move to option number two.
      2. My second option would be to implement a special repository method which returns the required data and call this method when I get the object in the service layer. I would make no other changes to my source code. Now, if this approach is also too slow and I can prove it, I would move to option number three.
      3. My last option would be to “admit defeat” and create a repository method which is used to find out if all shipments of an order are finished. Then I would move the business logic from my domain model class to the service layer and document why I made this decision.

      I think that although we should always aim to in a situation where the business logic is in the domain model, we should NOT be fanatical about it.

      If we cannot meet our performance requirements, it is clear that we have to do something about it.

      However, we should first be able to prove that moving business logic from the domain model to the service layer will improve the performance of our application. Also, we should always document why we made this decision (otherwise someone might move the business logic back to the domain model and we would be screwed).

      Reply
  • Petri: Reference your BankAccount example. It is much too simple to be used in a production system and because you’ve made it so simple the transfer behavior seems to be fine in the BankAccount class.

    But how would your example handle if the deposit step that is inside the BankAccount transfer method were to fail? Which could easily happen in a production system that must record the deposit in a data repository. Your domain model business logic does not have any transaction management and rollback capabilities. In your example if the deposit step fails your code has still withdrawn the amount from the from BankAccount.

    Complex business rules (such as transaction management, rollback behavior, etc) involving multiple steps and model objects are better implemented in the Service layer. Given the requirements of a production money transfer system I don’t see implementing the transfer logic in the model object as a best practice.

    I agree with your basic premise that our model classes should contain relevant behaviors and not just data fields but your example over sells what can be removed from the Service layer and put into the domain model.

    Bruce

    Reply
    • Bruce,

      I want thank for your comment. You did a great job critiquing my blog post and your comment had some great insights in it.

      Reference your BankAccount example. It is much too simple to be used in a production system and because you’ve made it so simple the transfer behavior seems to be fine in the BankAccount class.

      I agree. I did cut a lot of corners in that example because I wanted to create a simple example which demonstrates the problems often found from the service layer of a Spring web application.

      But how would your example handle if the deposit step that is inside the BankAccount transfer method were to fail?

      Oops. It seems that I left two flaws in it!

      Which could easily happen in a production system that must record the deposit in a data repository. Your domain model business logic does not have any transaction management and rollback capabilities. In your example if the deposit step fails your code has still withdrawn the amount from the from BankAccount.

      You are right. This can (and probably will) happen at some point and we must be able to deal with it.

      Complex business rules (such as transaction management, rollback behavior, etc) involving multiple steps and model objects are better implemented in the Service layer. Given the requirements of a production money transfer system I don’t see implementing the transfer logic in the model object as a best practice.

      I agree. We cannot (and we should not) move everything to the domain model classes. We need a component which controls the whole process and a service class is a natural place for this logic.

      The logic found from the service layer is often referred as application logic but this leads to the next question:

      What is the difference of application logic and business logic?

      I agree with your basic premise that our model classes should contain relevant behaviors and not just data fields but your example over sells what can be removed from the Service layer and put into the domain model.

      Exactly. I did that on purpose because I wanted to raise more discussion about this subject. To be more specific, I wanted to raise discussion about the difference of application logic and business logic.

      It seems that I did a good job.

      Reply
  • There are reasons to put business logic inside services, especially dealing with Spring / JavaEE.

    1. Services act as “mediators” between entities. Their purpose is to avoid circular dependencies between entities which, in turn, behave much more like DTOs than Naked Objects.

    2. Service are beans, so they can be injected / autowired. Entities are created with new() so their implementation cannot be swapped for testing.

    3. Entities are managed, Services are the managers. That’s why entities should not be aware of the Entity Manager (and without EM it’s difficult to write business logic)

    https://java.net/projects/jpa-spec/lists/jsr338-experts/archive/2011-06/message/24

    DDD it’s interesting but JPA is not meant to implement it somehow.
    Maybe it’s cool with other frameworks :-)

    Reply
    • 1. Services act as “mediators” between entities. Their purpose is to avoid circular dependencies between entities which, in turn, behave much more like DTOs than Naked Objects.

      This is a telltale sign of an application which has an anemic domain model.

      2. Service are beans, so they can be injected / autowired. Entities are created with new() so their implementation cannot be swapped for testing.

      Does this argument have anything to do with the place of the business logic?

      3. Entities are managed, Services are the managers. That’s why entities should not be aware of the Entity Manager (and without EM it’s difficult to write business logic)

      I agree that entities are managed by services but that does not mean that the business logic should be added to the service layer.

      Also, you can definitely write business logic without using the entity manager (although you need to use it for getting the domain model objects).

      Reply
      • > This is a telltale sign of an application which has an anemic domain model.

        Indeed, anemic domain model. That’s the paradigm JPA is supposed to implement.

        > Does this argument [DI] have anything to do with the place of the business logic?

        Of course. Dependency injection (where available, read, not entities) supports writing business logic wiring things up. Every dependency introduced inside an entity is far less manageable (ie. no way to swap implementations and stronger coupling).

        > I agree that entities are managed by services but that does not mean that the business logic should be added to the service layer.

        I’m not talking in general, I’m talking about Spring / JavaEE model which clearly is not planned to work with DDD. ie. is not clear how to “leverage the benefits of DI” where DI is not available at all (@Entity). Generally speaking DDD can be a better paradigm, but not with these frameworks.

        Reply
        • Of course. Dependency injection (where available, read, not entities) supports writing business logic wiring things up. Every dependency introduced inside an entity is far less manageable (ie. no way to swap implementations and stronger coupling).

          So your argument is that business logic cannot be added to domain model because that leads into stronger coupling between domain model classes and makes testing hard (because you cannot change the implementation)?

          I think that is impossible to create a domain model which does not have dependencies with other domain model classes inside the same module (or subdomain). Also, I think that stronger coupling is not such a huge problem as long as it does not spread outside the module.

          After all, isn’t the purpose of a domain model to model a very specific business model? Typically these business models have dependencies between different “entities”. Why should the domain model be any different?

          Also, it is possible to use test doubles when you are writing tests for your domain model. I agree that it might require some grunt work but I also think that the benefits of this approach makes this sacrifice justified.

          I’m not talking in general, I’m talking about Spring / JavaEE model which clearly is not planned to work with DDD. ie. is not clear how to “leverage the benefits of DI” where DI is not available at all (@Entity). Generally speaking DDD can be a better paradigm, but not with these frameworks.

          Here are some examples about using DDD with Java EE and Spring:

          I agree that DDD can be hard (I am definitely not a master of it yet) but it is not impossible if you use Java EE or Spring.

          Reply
  • Hello,

    Very interesting article, thanks a lot for sharing your thoughts.

    Here’s my two cents on your example :

    I really like the idea behind your implementation but I have to questions :

    1.On the BankAccount class what made your decision of not using a POJO for the account with just getters and setters for the iban and balance ?
    and leave the methods that operate on a single account inside the BankAccount class

    2.I don’t seem to understand why you’ve decided to implement the transfer method inside the BankAccount, wouldn’t have been sufficient to have it implemented only under BankAccountServiceImpl class?

    Sorry in advance if my questions are wrong, I’m stand to be corrected cause I’m here to learn ;)

    Reply
    • I asked a very simple question:

      Whose responsibility is it?

      If an operation changes the state of an object, it seems logical to put that code “near” to the changed object. In other words, the code should be put to the domain model class.

      However, like Bruce pointed out, we should not forget that we still need a component which manages the overall process. That component is the service class.

      By the way, if you are interested of learning more about Domain-driven design, you should read Domain-Driven Design: Tackling Complexity in the Heart of Software.

      Reply
  • So you are essentially proposing that we have cookies that bake themselves? Letters that send themselves? Cars that assemble themselves?

    Sorry, but that is wrong!

    Reply
    • Don’t you like cookies that bake themselves?

      What I meant was that we should find a balance between the logic which is found from the service layer (often referred as application logic) and the business logic which is put to the domain model.

      Reply
    • This is the best argument in the discussion :).

      Reply
      • I started reading Implementing Domain-Driven Design because of the feedback I received from this blog post (Ben’s comment was the biggest motivator).

        I have finished about 30 percent of that book but it advocates that we should divide our services in two groups: application services and domain services.

        The application services act as an entry point to the domain and takes care of stuff like transactions and security. The business logic is placed either to domain model or to a domain service. I find this idea pretty interesting.

        Anyway, I plan to write a new blog post which describes my revisited thoughts about the correct location of business logic. I just need some time to think about it.

        Reply
  • Petri, nice article

    I have recommended it to my followers on a FaceBook page =)
    https://www.facebook.com/fruzenshtein.notes

    Reply
    • Thanks Alexey! I really appreciate your gesture.

      Reply
  • Thank you Petri, quite nice article!.
    Maybe there’s no a unique bullet proof solution, but this makes
    me think more about software architecture/design and patterns.
    Thanks again for sharing!

    Reply
    • Hi Gerardo,

      You are welcome. I am happy to hear that this article raised some thoughts which you can use in your work.

      This might not be the silver bullet but sometimes it is useful to evaluate our current way of doing things and maybe try something which seems a bit radical. I think that this is the only we can learn and improve our skills.

      Reply
  • Petri,
    Firstly excellent website! I found it while searching around for testing Spring MVC applications.
    I’m currently questioning where business logic is best placed within an application. I’d be interested to hear your thoughts on implementing business validation logic in a validator class that implements Validator interface.

    Reply
    • Hi Declan,

      Thank you for your kind words. I really appreciate them!

      The Validator interface brings back a lot of memories. I think that the last time when I used it was with Spring 1.x and 2.x (before Spring added support for JSR-303 based validation). At that time I used it only for validating the information of form objects.

      Nowadays I follow these guidelines:

      • Validate form objects in the web layer. I use JSR-303 validation because it keeps the code of my controllers cleaner. The downside is that I have to rely on “annotation magic” but I can live with it.
      • Domain model classes are responsible of keeping the domain model in a “legal” state. In other words, I start by trying to add the “business logic” to the domain model (this includes validation). However, sometimes I notice that this isn’t possible or practical.
      • If all else fails, add “business logic” to the service layer (again, this includes validation).

      When I follow these guidelines I get a nice separation of domain logic and business logic. The difference of these two is that the business logic might need access to external resources such as other services and so on.

      About the Validator interface. I haven’t used it for this purpose but now that you mentioned it, I started to think that it might make sense! Could you share your thoughts on using a validator class? What kind of validation logic would you add to these validator classes?

      Reply
      • Sorry for the delay in replying, I didn’t receive any notification about the reply :-)

        I agree the JSR-303 is handy for the basic validation of the form e.g @NotEmpty etc and I do use them but say you have a simple form (say 2 fields) but the validation logic behind it is more complex.

        The two fields have to match in the database so you try to load the domain object based on these values. If found you then need to pass off to the validator which may call another service as part of the validation. Any failed validation is then added to the Errors object passed in via the Validator.validate method.

        If the two fields don’t match then the validation doesn’t need to be called.

        Reply
        • Hi Declan,

          I agree that the JSR-303 is not a good choice in your situation. I have typically handled similar situations by following these steps:

          1. Add custom code to the service layer. This code validates the business rules and throws an exception if the validation fails.
          2. The controller class catches the exception and returns the correct validation error message.

          However, now that you mentioned it, adding this code a to component which implements the Validator interface sounds like a good idea. The problem of my current approach is that handling exceptions is not very practical if you have to enforce multiple validation rules. Implementing the Validator interface seems to solve this problem. Is this one of the reasons why you consider this approach?

          I would probably still do this kind of validation in the service layer because it requires database access. Do you have any thoughts about this?

          P.S. I will try to figure out a good way to solve the email notification issue.

          Reply
          • The service classes are injected into the Validator classes so you can access them as required. You basically get another layer.

            Haven’t focused much on writing tests for these but I guess having the validation logic in one place would be easier to test.

  • Great article. Often it is wrongly assumed that merely using frameworks ensure good design automatically without involving a competent architect to work on how the system should be designed.

    Reply
    • I agree that a framework is just a tool. It cannot guarantee good design automatically because every tool can be used in the wrong way.

      On the other hand, I think that leaving the architectural decisions to a single person is a risk because an architect might seem competent when he/she is in fact an expert beginner.

      Of course this might work a good decision too, but I think that every team member should participate to the architecture design.

      Reply

Leave a Comment