The Biggest Flaw of Spring Web Applications

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.

54 comments… add one
  • Jari Timonen Jun 20, 2013 @ 15:31

    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.. :)

    • Petri Jun 20, 2013 @ 16:03

      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.

    • Golfman Aug 22, 2017 @ 10:15

      Why do you say ORMs killed proper domains? We have done proper domain models (NOT anemic) for years which are transparently persisted by the ORM we use.

      If the persistence engine (ORM) is transparent it can't affect your domain modeling otherwise it can't be called transparent..

      I guess when you refer to ORMs in general you are actually referring to the most popular one which has a hard time persisting any non trivial domain model in a nice, clean, pure, workaround free, caveat free, performant manner.

      Even though we started with the most popular one we quickly moved away from the "sheeple's choice" to one that actually supports truly transparent persistence: DataNucleus. Try it - you'll like it!

      • Petri Aug 22, 2017 @ 20:12

        I doubt that you will get an answer to your question since the original comment was posted over four years ago, and I assume the person who posted it won't come back to read your comment. :(

        It's a shame because it would be interesting to see his answer.

  • Frisian Jun 20, 2013 @ 17:31

    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.

    • Petri Jun 20, 2013 @ 22:39

      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!

  • Tom Jun 21, 2013 @ 7:55

    "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.

    • Petri Jun 21, 2013 @ 11:11

      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!

      • Karl Kovaciny Nov 13, 2016 @ 15:20

        What an instructive comment. Thank you.

        • Petri Nov 16, 2016 @ 22:06

          You are welcome!

      • @Alonso_Isidoro Sep 14, 2020 @ 15:06

        why not make it public returning a boolean? The other service can have injected this service, no need to write business methods in pojo class. ¿What happen if BankAccount changes?
        private boolean hasSufficientFunds(BankAccount account, double requiredBalance){

        return (account.getBalance() < requiredBalance));
        }

  • Mihhail Jun 21, 2013 @ 13:44

    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???)?

    • Petri Jun 21, 2013 @ 14:18

      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).

  • Bruce Jun 21, 2013 @ 15:16

    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

    • Petri Jun 22, 2013 @ 11:55

      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.

  • gpilotino Jun 21, 2013 @ 15:39

    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 :-)

    • Petri Jun 22, 2013 @ 13:14

      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).

      • gpilotino Jun 22, 2013 @ 17:47

        > 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.

        • Petri Jun 22, 2013 @ 21:55

          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.

  • lucio Jun 21, 2013 @ 18:20

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

    • Petri Jun 22, 2013 @ 14:45

      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.

  • Ben Sounders Jun 21, 2013 @ 20:42

    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!

    • Petri Jun 22, 2013 @ 14:36

      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.

      • James Woods Aug 6, 2014 @ 6:00

        "So you are essentially proposing that we have cookies that bake themselves?"

        For your example if you apply object oriented modelling you'd have an Oven domain class with a bake() method that you pass a Cookie object as an argument.

        The idea being proposed here is to think about objects and how they interact first, design domain classes to suit, and then use services as the access mechanism to the object domain. It all centres around 'maximising' the 'cohesion' of the software. The data structures and the logic to modify them are a given, the choice is where to place the logic. If you follow a purely procedural model of development then the logic is completely separated from the data structures. If you follow an object-oriented model of development then the logic is tightly bound to the data structures. Without doubt both approaches can produce working software, but procedural code is not object oriented and by moving the business logic to a service you are making the code procedural.

        • marcellus48k Jun 10, 2016 @ 0:34

          Hi, change oven domain class for oven service class and you have your object oriented code again. No drama at all.

    • Pavel Horal Jun 29, 2013 @ 11:21

      This is the best argument in the discussion :).

      • Petri Jun 29, 2013 @ 11:32

        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.

  • Alexey Zvolinskiy Jun 24, 2013 @ 23:15

    Petri, nice article

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

    • Petri Jun 25, 2013 @ 0:26

      Thanks Alexey! I really appreciate your gesture.

  • Gerardo Velez Jul 1, 2013 @ 11:41

    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!

    • Petri Jul 1, 2013 @ 13:27

      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.

  • Declan Butler Jan 22, 2014 @ 1:23

    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.

    • Petri Jan 23, 2014 @ 20:39

      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?

      • Declan Butler Jan 30, 2014 @ 17:11

        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.

        • Petri Jan 30, 2014 @ 22:43

          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.

          • Declan Butler Feb 11, 2014 @ 21:45

            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.

  • Harish Jun 2, 2014 @ 8:29

    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.

    • Petri Jun 4, 2014 @ 19:59

      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.

  • Kevin Aug 16, 2014 @ 0:36

    Just stumbled on this great post. IMHO, frameworks like Spring and Hibernate are a big reason that the vast majority of Java programmers don't truly understand object oriented programming. They think they are doing OOP, because hey, they have their model objects, and their service objects that operate on them and so forth...but it's really all just procedural in design. You could easily translate it into C, were the models are structs and the service classes are just groups of functions. The only thing that having them as objects buys you is that you can swap out service objects for testing. Really, that's all.

    This style of development is extremely pervasive--it seems to be the norm rather than the exception from what I've seen, which is really unfortunate. What's the point of using an OO language?

    • Petri Aug 20, 2014 @ 21:18

      Thank you for writing this comment. It is really interesting and you make a valid point.

      Most business web applications that I have seen, organizes functions as transaction scripts, and it is easy to see that you could replace an object-oriented language with a procedural language (without sacrificing anything).

      I have to confess that I am not sure if I understand object-oriented programming but at least I know this. I think this gives me a real opportunity to become a better developer.

      What’s the point of using an OO language?

      I think that most developers don't choose the used programming language because it is procedural, object-oriented, functional, or what ever. It seems that most developers use language X because they learned it in the university.

      Also, if we think about Java, one reason why it is so popular is that it has a LOT of libraries and frameworks. If the situation would be different, no one would use it just because it is object-oriented.

  • Benoit Oct 16, 2015 @ 16:29

    This article is very interesting and it shakes my view of the way I design applications.

    I will think about the Anemic Domain Model anti-pattern and about Domain Driven Design in my future architecture decisions, but I don't perfectly see how to do it in practice yet.

    One of the biggest problems I see is about the nastier casew when the business logic can't completely be placed in the domain layer.

    Consider for example an application that manages programs and provides the ability to compile and execute them.

    An application that's fallen in the anemic domain model pattern might have a Compilation service, which would have a "compile(Program p)" method. This method performs a number of operations which change the state of the Program according to some business rules, and it also calls another service which will perform the actual compilation based on the infrastructure used (for example, it could be a DockerManagementService, providing the ability to launch a container with the appropriate gcc version on it, and then transfering the source code on this container and launching gcc).

    It's quite easy to move the business logic of the status change of the Program to the domain layer. However, how do you suggest to deal with the other service? It doesn't seem right to me to inject it in the domain class itself. I don't think the domain layer should call the service layer, even if it contains more logic and is not anemic anymore.

    Basically, my problem is that I see very well how to bring back the domain-related logic in the domain layer, but most complex applications have a lot of logic that is external to the domain layer. It seems that it's appropriate to leave it in the service layer, but it complexifies the interaction between service layer and domain layer, and I'm not sure I see how to deal with that.

    • Petri Oct 18, 2015 @ 15:18

      Hi,

      This article is very interesting and it shakes my view of the way I design applications.

      Thank you for kind words. I really appreciate them.

      It’s quite easy to move the business logic of the status change of the Program to the domain layer. However, how do you suggest to deal with the other service? It doesn’t seem right to me to inject it in the domain class itself. I don’t think the domain layer should call the service layer, even if it contains more logic and is not anemic anymore.

      You can use domain services for this purpose. A domain service is a stateless class that provides operations which are related to a domain concept but aren’t a “natural” part of an entity or a value object. I suggest that you read this blog post and take a look at this comment.

      If you have any additional question, don't hesitate to ask them!

  • drc May 2, 2016 @ 6:30

    Interesting article. Thanks for writing it.

    How does having the business logic in the domain model impact unit testing?

    • Petri May 4, 2016 @ 22:25

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

      How does having the business logic in the domain model impact unit testing?

      Well, it makes unit testing easier because now I can write unit tests for my business logic without invoking an application service.

  • Jeff Nov 4, 2016 @ 23:50

    Thanks for this article. I wonder if you would agree that it addresses the following situation:

    I am attempting to adapt an existing Spring MVC application (Enhanced Pet Clinic which is an extensive sample project illustrating many aspects of the Spring framework) and the new persistent classes I added I attempted to include business behavior in -- the old classes, like Pet and Owner are data-only classes.
    The new application has classes like AWSInstance and some methods that I included were start() and stop() which made AWS API calls. This seemed to work okay until I added methods that need to use the existing Clinic service. The problem arises when attempting to inject this service into newed-up instances -- it can't be done because Spring knows nothing about such instances.
    I was told that the correct design is in fact to include no business logic in persistent instances and instead put this logic in services.
    Since indeed I need to new-up objects like Instance and Pet (in the old application) it seems like there is no way I can include business logic that would require accessing services.
    So my question at this point is, is your article asserting that Spring MVC can be used with objects that have logic (are not data-only) or is there a fundamental flaw in Spring MVC that precludes this?

    • Petri Nov 6, 2016 @ 22:37

      Hi,

      It's true that Spring container can inject dependencies only into such objects that are managed by it. That is why you cannot automatically inject dependencies into your entities because they are managed by the framework which you use to persist / retrieve them.

      That being said, if you absolutely want to use a Spring bean from an entity, you can always pass the required bean as a method parameter or you can use a JPA entity listener.

      As you probably figured out, you can add logic into your entities, but it might not be a good idea to do it IF this logic needs to use an external resource. Typically Spring developers solve this problem by using one of these two options:

      1. They add everything into a service class.
      2. They add code, which interacts with external resources, into the service class and add the actual business logic into the domain model.

      I prefer the second option, but the first option has its benefits too. If you are interested in DDD and Spring, you should take a look at the following links:

      • Jeff Nov 10, 2016 @ 0:18

        Thanks.

  • Rodney Barbati Jun 22, 2017 @ 0:04

    I absolutely agree with the premise of this article - all of the large spring apps I have worked on have been nightmares of spaghetti code tossed around in services, with domain objects being passed around like low cost hookers. Copy and paste is the word of the day in these systems.

    My own solution to such is to make actual objects out of the beans prior to using them (objects are data and code, not just one or the other) - in other words, if I send a Transaction bean (just data) over the wire to the server, I immediately wrap that Transaction in a class that provides the desired functionality. By wrap it, I mean I pass the Transaction (data only) into the functional wrapper in its constructor call. The functional wrapper uses the data object as its only (or at least primary) data source.

    This provides the control of state changes and encapsulation of functionality desired, while still providing a completely normal Spring experience of using data beans for persistence and service calls.

    Using this technique results in not needing the thousands of lines of code dealing with copying entity beans to other classes or structures, the passing of entity beans from service method to service method, where each method retrieves the beans data via its getters() and operates on that data itself.

    This also has the added benefit of allowing for inheritance of behaviors and separation of responsibility by providing various wrappers that each provide a distinct set of functionality for their single entity bean data member.

    And finally, code written like this is very thread safe - every thread creates its own instance of the wrapper passing in the entity bean as its data member to the constructor. The wrapper code operates directly on the provided bean instance.

  • Michael Dec 2, 2018 @ 15:41

    We as developers forget one thing. And that thing is that WE chose to implement something, not the framework. The framework lives in the low level boundary layer of a system. Business rules should indeed not be coupled to database entities. Database entities live also in the disposable boundary layer, while business rules are the most high level entities of the system. That said, a framework may or may not supply a solution for implementing proper business rules. If it does not, it is not perse the frameworks fault, it is the developers fault. The framework is the boundary, and that is all there is to the framework. So do not blame it on the framework. A business rule is never bound to anything low level, let alone a framework. So it is up to US. And there is another problem, US are not US, because we don't unite, we don't value principles, patterns and process the same way. While I'm all for the Agile way, others simply want to work quick and dirty.

  • Mohammad Jan 3, 2019 @ 14:50

    Hi
    I'm not a very experienced developer but I can come up with 3 reasons to put logic in service layer when using Spring (Dependency injection) in a layered way:
    1- I can see each Service class as a facade that brings everything below it together in order to shape the complex/complicated behavior of system. Different layers that will be used in Service layer are repository, client, utils, helpers and ... . If you inject them to domain class, it is not a domain anymore. It is a highly coupled class which I do not know a name for.
    2- With Services we can easily change the behavior of of system without need to change the definition domains. (Kind of high cohesion)
    3- Its all about how de coupled you want to be in terms of code, design, definition, point of view and
    ... .

    tiroon.ir

  • Marc May 27, 2020 @ 3:06

    very bad idea to put logic in domain... you break sing responsability rule...

    • Petri May 27, 2020 @ 22:41

      Interesting point. I would like to hear your answers to your these two questions:

      1. What is your definition of the single responsibility principle?
      2. How does adding logic to domain model break your definition of that principle?
  • slsi Jan 25, 2024 @ 7:29

    I agree. The object must be like an object. We should remember that we are in object-oriented-programming rather than spring-oriented-programming.

Leave a Reply