• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Tim Cooke
  • paul wheaton
  • Jeanne Boyarsky
  • Ron McLeod
Sheriffs:
  • Paul Clapham
  • Liutauras Vilda
  • Devaka Cooray
Saloon Keepers:
  • Tim Holloway
  • Roland Mueller
Bartenders:

business logic in jpa entities?

 
Ranch Hand
Posts: 120
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
What is the best practice in regards to placing business logic? If I have the following 2 Entities:




Where would be the best place to put code to check if a user is a member of a project? In one of the entities? In a ProjectService (currently deals with the business logic for persisting Project Entities) class? Or somewhere else?

A User will have less Projects then a Project will have Users, thus if I was to put it in an Entity class, the User Entity seems like a better choice. However my concern is that my Entities would be too big. I have getters and setters, equals and hashCode methods as well as builder classes in there currently. My Project entity is 240 lines of code currently.

Any thoughts would be appreciated.

Thanks
 
Ranch Hand
Posts: 10198
3
Mac PPC Eclipse IDE Ubuntu
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Your JPA entities should just represent the domain model! Ideally all the business logic should go into the business tier, which sits above your domain model.
 
Saloon Keeper
Posts: 28468
210
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Putting business logic in JPA Entities is Very Bad.

Putting business logic in JPA DAOs is also Bad.

I actually run 2 logic tiers in my JPA projects. The DAO tier does strictly CRUD/find operations. Usually against a single table, but occasionally on a table with children, if it makes sense to do so.

The higher level (service) tier handles my inter-table relationships. It has the persistence-related business logic (such as validating users and the groups that they are in). It's also the boundary between the general business logic and the persistence logic. The persistence services are transactionally bound. Anything above them is detached data and non-transactional. Anything within/below them is transactional and attached (if needed). The DAOs do the grunt work for the services, and they inherit the transactional context of the services that invoke them.

This makes for a nice tidy arrangement where I know exactly where to look for a given functionality, can easily modify and debug it, and makes the various layers plug-replaceable for better code re-use capabilities. Entities in particular can start being used in one app and subsequently find utility in many other apps, which is why app-specific business logic in the entities is especially bad.
 
Rj Ewing
Ranch Hand
Posts: 120
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Tim Holloway wrote:Putting business logic in JPA Entities is Very Bad.

Putting business logic in JPA DAOs is also Bad.

I actually run 2 logic tiers in my JPA projects. The DAO tier does strictly CRUD/find operations. Usually against a single table, but occasionally on a table with children, if it makes sense to do so.

The higher level (service) tier handles my inter-table relationships. It has the persistence-related business logic (such as validating users and the groups that they are in). It's also the boundary between the general business logic and the persistence logic. The persistence services are transactionally bound. Anything above them is detached data and non-transactional. Anything within/below them is transactional and attached (if needed). The DAOs do the grunt work for the services, and they inherit the transactional context of the services that invoke them.

This makes for a nice tidy arrangement where I know exactly where to look for a given functionality, can easily modify and debug it, and makes the various layers plug-replaceable for better code re-use capabilities. Entities in particular can start being used in one app and subsequently find utility in many other apps, which is why app-specific business logic in the entities is especially bad.



In this case, would you put a function to check if a User is a member of a Project in the (service) tier or even higher, as the service tier is only for persistence logic?
 
Tim Holloway
Saloon Keeper
Posts: 28468
210
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Probably. The deciding factor is lies in how many trips to the database will be required. The point of the service layer methods is that I can build/manipulate/reference a working set of related records all within a single transaction (and connection). That facilitates both efficiency and data integrity.
 
Rj Ewing
Ranch Hand
Posts: 120
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
do you know of any projects you would recommend looking at to get a better idea of project structure and how the different layers work together?
 
Tim Holloway
Saloon Keeper
Posts: 28468
210
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Yes I do. Mine.

Unfortunately, all the best ones are proprietary apps that I can't publish.

I have a "technology sandbox" app that's open for inspection, but it was primarily developed before I had this particular architecture well-defined, so it's more of an embarrassment than a shining example.
 
Sheriff
Posts: 11604
178
Hibernate jQuery Eclipse IDE Spring MySQL Database AngularJS Tomcat Server Chrome Java
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Rj Ewing wrote:do you know of any projects you would recommend looking at to get a better idea of project structure and how the different layers work together?


I think it will be hard to find one (very) good project as an example because most apps are proprietary and thus can't be published. If I had to develop such a business requirement, I would probably end up with a project structure like this:This gives an idea about a possible project structure. I am relying heavily on the Spring Framework and for the DAO tier I used Spring Data JPA. Those repositories could easily be replaced with DAOs as well. The database will be properly defined, meaning all required primary keys, foreign keys and possible constraints will be created.

Hope it helps!
Kind regards,
Roel
 
Tim Holloway
Saloon Keeper
Posts: 28468
210
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Well, maybe someday I'll get my sandbox app updated. It's a good application for this sort of thing, just didn't have a good set of rules formulated when I did most of the work and none of the literature at the time did more than get me aimed in the right direction.

My sandbox app is a bus route management system. Its principal entities are bus stops, bus routes, bus schedules and bus "itineraries". A schedule is a set of itineraries for a given day. An itinerary is a collection of stops and their arrival/departure times, covering both inbound and outbound trips.

It's written in JSF and since in JSF my policy is that the JSF code and its business methods only work with detached objects, it's the job of the Persistence Service layer to create, retrieve, and update working sets. For example, the trip editor allows me to select bus stops, put them in sequence, and associate a time with the arrival at and departure from each stop on the trip. So the working set is a single trip entity and its stops. The service function to fetch all of this is responsible for invoking the Itinerary DAO to lookup the trip (Itinerary) object, and lazy-fetch the stop objects for it before the whole thing is detached and presented as a POJO tree to the JPA business logic. Likewise, the Itinerary save() method takes the modified tree and posts it back (as a single transaction) to the affected tables.

I use Spring JPA as well, and one thing that their documentation never made clear was the precise meaning of the @Repository annotation - it's presented as fairly abstract, but I couldn't narrow it to specific concrete qualities. Regardless, in practice, it allows auto-wiring JPA, so both my service tier classes and my DAO-tier classes carry @Repository and @Transactional on their class definitions.
 
Rj Ewing
Ranch Hand
Posts: 120
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

It's written in JSF and since in JSF my policy is that the JSF code and its business methods only work with detached objects, it's the job of the Persistence Service layer to create, retrieve, and update working sets. For example, the trip editor allows me to select bus stops, put them in sequence, and associate a time with the arrival at and departure from each stop on the trip. So the working set is a single trip entity and its stops. The service function to fetch all of this is responsible for invoking the Itinerary DAO to lookup the trip (Itinerary) object, and lazy-fetch the stop objects for it before the whole thing is detached and presented as a POJO tree to the JPA business logic.



If you needed to work with the stop objects for a trip in the business layer, you would call the service layer again, passing in the trip object) to load the Stops. Then do work on the stops?
 
Rj Ewing
Ranch Hand
Posts: 120
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

This gives an idea about a possible project structure. I am relying heavily on the Spring Framework and for the DAO tier I used Spring Data JPA. Those repositories could easily be replaced with DAOs as well. The database will be properly defined, meaning all required primary keys, foreign keys and possible constraints will be created.



Thanks, this helps really me visualize the project structure.

How do you prevent having a lot of dependencies injected into a class? If I have more then say 3-4 dependencies injected, is this a sign that my class is doing too much and should be split into 2+ classes?
 
Tim Holloway
Saloon Keeper
Posts: 28468
210
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
This is an app that does much its work against a primary table and its attached resources, where there's a list view and a detail edit view for each primary table. So when listing stops on a trip detail view, you'd click on a stop and that would navigate you to the stop detail editor.

Actually, I noticed that for one reason or another, I don't do it that way at the moment. Here's the actual route editor view.

screenshot-www-mousetech-com-2016-05-13-12-34-28.png
[Thumbnail for screenshot-www-mousetech-com-2016-05-13-12-34-28.png]
Route editor view
 
Tim Holloway
Saloon Keeper
Posts: 28468
210
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I have some apps with a multitude of services injected into them, although most of the classes of that stripe are aggregators for higher level functions. In fact, a Persistence Service method might have half a dozen DAOs injected into it, depending on how complex the working sets were.

The main criteria I use is that a class should do things in a straightforward manner. If it gets too complicated, then that's often a signal that something needs simplifying and the number of beans wired in is just one more symptom of a general problem.
 
Rj Ewing
Ranch Hand
Posts: 120
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
So I'm running into a LazyInitializationException issue. So if a User is logged in, I fetch the User object at the beginning of the request. Then I am adding the User to a ProjectChild entity.

Before saving the new ProjectChild entity, I need to verify that the user is a member of the parent Project.





I am getting the exception when calling User.getMemberProjects().
I think this is because I am first fetching the User when the request begins. Thus it is a seperate Transaction then when I am saving ProjectChild object. Do I need to manually merge the User object? I'm using spring data jpa. Is there a way to automatically merge in this situation?
 
Roel De Nijs
Sheriff
Posts: 11604
178
Hibernate jQuery Eclipse IDE Spring MySQL Database AngularJS Tomcat Server Chrome Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Rj Ewing wrote:I am getting the exception when calling User.getMemberProjects().
I think this is because I am first fetching the User when the request begins. Thus it is a seperate Transaction then when I am saving ProjectChild object. Do I need to manually merge the User object? I'm using spring data jpa. Is there a way to automatically merge in this situation?


The LazyInitializationException is probably the most thrown exception when you are working with an ORM (and/or JPA). Probably everyone working with these technologies has encountered this exception.

And the cause is (probably) very simple: when you nvoke the validate() method, the collection with memberProjects of the User object is not yet initialized, because it's configered to be lazy loaded. When you invoke the method getMemberProjects(), the ORM wants to retrieve the memberProjects from the database but it requires a transaction. Because there is no transaction, the LazyInitializationException is thrown.

I wonder how the create() method is invoked and how the pc object is created. Because I would expect to see a different create() method signature, e.g. create(long projectId, long userId). I am also wondering about the purpose of having the ProjectChild class, because that class didn't exist in your first post.

And you are trying to validate a business rule using the entities, but you could define for example a method in your repository as well to verify if this project is already assigned to this user. And a final remark: you are using a set to store users (project members). One of the key features of a set is that all elements are unique...

Hope it helps!
Kind regards,
Roel
 
Rj Ewing
Ranch Hand
Posts: 120
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Roel De Nijs wrote:
The LazyInitializationException is probably the most thrown exception when you are working with an ORM (and/or JPA). Probably everyone working with these technologies has encountered this exception.

And the cause is (probably) very simple: when you nvoke the validate() method, the collection with memberProjects of the User object is not yet initialized, because it's configered to be lazy loaded. When you invoke the method getMemberProjects(), the ORM wants to retrieve the memberProjects from the database but it requires a transaction. Because there is no transaction, the LazyInitializationException is thrown.

I wonder how the create() method is invoked and how the pc object is created. Because I would expect to see a different create() method signature, e.g. create(long projectId, long userId). I am also wondering about the purpose of having the ProjectChild class, because that class didn't exist in your first post.



I didn't include the entire context in the first post. My end goal is to create a ProjectChild object. Only User's that are a member of the parent Project can create ProjectChild objects, thus why my original question was asking about validating if a User is a member of a Project.

I am using builders to create the objects in my REST services. I pass in the User and Project to the ProjectChild.builder. I am then trying to persist the ProjectChild in ProjectChildService.create bean. Here is some sample code:





See my last post for examples of ProjectChildService and ProjectValidator.

Thanks again for the help!
 
Tim Holloway
Saloon Keeper
Posts: 28468
210
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Actually literally what happens is that when you fetch an object with lazy-load relations, instead of the actual object reference, what you get back in that property is a placeholder that throws the exception when accessed.

That's part of why my Service methods work with working sets of objects not just single objects. The Service method is still within Transactional context (connected), so I can force-load the related objects of interest in the event that their normal mode is lazy fetch.

I also have been known to widen a working set from a business method if it determines that rarely-used or voluminous data that wasn't previously fetched, if required. I'll either refetch using the narrow set's primary object ID, or pass the narrower set to the widening Service method and let it force-load, then return the widened object. Note that such methods may potentially involve returning a different base POJO than the one that they were passed. For example, a JPA merge operation often does that.

If I have a LOT of related objects - for example, if I want to add an employee to a very large department, my Service method may include as its parameters the Employee working set and a basic Department entity object (lazy-fetch on Employees, so no actual unrelated Employees loaded). That allows me to try and save on resources by doing things I can only do within a Transactional, connected context. Such methods WOULD invalidate both the original Employee entity and Department entity, so if I wanted to do further work at the business object level, I'd either have to return the new instances or re-fetch them as any attempt to do further persisting of the original detached objects would throw an Exception. To be precise, I think it would be ConcurrentModificationException.
 
Rj Ewing
Ranch Hand
Posts: 120
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Tim Holloway wrote:Actually literally what happens is that when you fetch an object with lazy-load relations, instead of the actual object reference, what you get back in that property is a placeholder that throws the exception when accessed.

That's part of why my Service methods work with working sets of objects not just single objects. The Service method is still within Transactional context (connected), so I can force-load the related objects of interest in the event that their normal mode is lazy fetch.

I also have been known to widen a working set from a business method if it determines that rarely-used or voluminous data that wasn't previously fetched, if required. I'll either refetch using the narrow set's primary object ID, or pass the narrower set to the widening Service method and let it force-load, then return the widened object. Note that such methods may potentially involve returning a different base POJO than the one that they were passed. For example, a JPA merge operation often does that.

If I have a LOT of related objects - for example, if I want to add an employee to a very large department, my Service method may include as its parameters the Employee working set and a basic Department entity object (lazy-fetch on Employees, so no actual unrelated Employees loaded). That allows me to try and save on resources by doing things I can only do within a Transactional, connected context. Such methods WOULD invalidate both the original Employee entity and Department entity, so if I wanted to do further work at the business object level, I'd either have to return the new instances or re-fetch them as any attempt to do further persisting of the original detached objects would throw an Exception. To be precise, I think it would be ConcurrentModificationException.



So trying to understand this and translate it to my example code above....

Passing in a detached User and Project object to the ProjectChildBuilder is acceptable.
Somewhere I am going to need to widen the User or Project object to fetch either the Project's Users or the User's Projects.

This is where I get a bit confused. Is it better to widen the set in the service who calls ProjectValidator.validate() or annotate ProjectValidator with @Transactional and widen the set there, or have a new service just for widening one entity object?


Another question that comes to mind, is when is it appropriate to eager load collections? Is there a general rule like if a collection is going to contain less then 5 objects 90% of the time, then use eager loading?
 
Tim Holloway
Saloon Keeper
Posts: 28468
210
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Likes 3
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Answering your last question first, eager loading is something that you only want to do in cases where both primary and foreign objects will always be used together. That might be objects with a strict 1:1 relationshop, or 1:many where the "many" is a low number (less than 5, perhaps). As the number of objects getting pulled in goes up, the eagerness generally gets less. And don't forget that if you are pulling in large sets of primary objects, the effect of eager-fetching foreign dependencies will be amplified! So the rule is simply to avoid eager fetches where they'll be bringing in resources you either won't use or don't have space for.

If I want to do something like add an employee to a department, I probably wouldn't widen my working set. Instead, I'd usually edit the data and then send it back wholesale for the persistence service to handle. In fact, I might not even fetch the department record at all. It might be sufficient simply to pass department ID into a persistence service like AddMoveEmployeeToDepartment(employee, departmentID). That gives me an opportunity to more behind-the-scenes data optimization. If the department record contains columns such as "numberOfEmployees" that can be computed by the service method, they, too would be covered that way. Only if I actually needed application-specific things done would I fetch the Department record back to the business layer.

Widening is something that I usually only do if it will facilitate the construction of a UI display model. Otherwise, I typically work with the various entities individually.

One useful trick for working within a JPA transaction is that JPA supports the retrieval of entity references. Basically, a reference retrieval returns a handle to the entity without actually fetching the entity from the database. You can use the handle just like an actual entity instance, and it will behave like the entity, but without the local overhead. You can then use this reference to obtain the actual entity without doing an explicity find operation.

Anyway, in reference to your specific thread question. If all I wanted to do was determine whether an employee was a member of a department and the employee entity had a lazy-fetch stub for its department property, my service layer would simple define "boolean hasEmployeeDepartment(employee)" and return true/false. If I actually needed the department record, I'd define either "Department getDepartmentForEmployee(employee)" and treat the Department and Employee as separate entities or if it was more useful, I'd widen Employee to resolve its department property with "Employee getEmployeeWithDepartment(employee)".

Note that since in all of the above cases, the Employee is the principal object, these methods would be in the EmployeeService class. If I had a Department and wanted to force-fetch retrieval of its employees, that would be done in the DepartmentService, for example with a function such as "Department fetchEmployeesForDepartment(department)", which is a widening function. Applied to my Bus Route demo app, that would be used in cases where, for example, I had a detail view of a route and I wanted to expand it to list the Route Stops.
 
Rj Ewing
Ranch Hand
Posts: 120
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Tim Holloway wrote:
If I want to do something like add an employee to a department, I probably wouldn't widen my working set. Instead, I'd usually edit the data and then send it back wholesale for the persistence service to handle. In fact, I might not even fetch the department record at all. It might be sufficient simply to pass department ID into a persistence service like AddMoveEmployeeToDepartment(employee, departmentID). That gives me an opportunity to more behind-the-scenes data optimization. If the department record contains columns such as "numberOfEmployees" that can be computed by the service method, they, too would be covered that way. Only if I actually needed application-specific things done would I fetch the Department record back to the business layer.

Widening is something that I usually only do if it will facilitate the construction of a UI display model. Otherwise, I typically work with the various entities individually.

One useful trick for working within a JPA transaction is that JPA supports the retrieval of entity references. Basically, a reference retrieval returns a handle to the entity without actually fetching the entity from the database. You can use the handle just like an actual entity instance, and it will behave like the entity, but without the local overhead. You can then use this reference to obtain the actual entity without doing an explicity find operation.



The references is interesting. So if you were creating a new employee, would you pass in a Department entity reference or the departmentID to the Employee builder? Or would you create an employee and then call an addEmployeeToDepartment(employee, departmentID) method?

If the later, how would you prevent Employee entities from being instantiated in an invalid state (no department)?
 
Tim Holloway
Saloon Keeper
Posts: 28468
210
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Employee, as a JPA POJO, would typically be constructed the way any other POJO would - by filling in the blanks of an Employee created by one of its constructor methods. Or, of course, a Factory could do it, if that worked better.

You can't really use a JPA reference outside of the connected (Transactional) environment. So I'd usually be passing the Department ID to the Employee method's save service. That way I wouldn't have to always fetch the entire department record first unless I needed to - since the Department ID is, of course, a property of the Department record, but not vice versa.
 
Rj Ewing
Ranch Hand
Posts: 120
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
ah. So any relationships, you are going to be dealing with them in the service layer. That makes sense.

I was retrieving the related objects in the service layer, then creating my new entity. Then I would pass the new entity with related objects to serviceLayer.save to be persisted. It seemed like a lot of trips to the db.

So now I'll create a new entity from the request form data. Then pass the entity as well as and related entity ID's into my serviceLayer.save method.

Thanks for all the help and explanation!
 
Tim Holloway
Saloon Keeper
Posts: 28468
210
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
That's basically what the service layer is for. The DAO layer handles per-table operations, the Service layer handles inter-table relationships and database-related business logic. In some simple cases, then, the Service layer is just a pass-through to the DAO, but by being consistent, I always know where to look for stuff and the extra overhead of not having the GUI business invoke DAOs directly is inconsequential.
 
Greenhorn
Posts: 5
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Tim Holloway wrote:Putting business logic in JPA Entities is Very Bad.

Putting business logic in JPA DAOs is also Bad.


Why ? Is anemic domain model good (http://www.martinfowler.com/bliki/AnemicDomainModel.html)? And what do you think of http://www.adam-bien.com/roller/abien/entry/should_jpa_entities_contain_business?
 
Tim Holloway
Saloon Keeper
Posts: 28468
210
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
The reason that putting business logic into the Entities and DAOs is BAD is precisely the same reason why I also recommend avoiding the free use of Stored Procedures in the Database.

By tying specifics of the application to the persistence layers, you reduce the ability to recycle these objects in other applications that may use the same data but have different business requirements. I got a tast of this firsthand on a system that I started supporting about 5 years back. They had invented their own (rather limited) ORM and it was laced with application logic in its entities and DAOs. They reached all the way up to the presentation layer, which made them utterly useless for batch utility programs. As a matter of fact, the original webapp would have been better implemented as 2 separate webapps, but thanks to all the app-specific logic in the ORM code, it wasn't possible to cleanly do so. My ultimate solution was to ditch all that stuff and rewrite it in JPA according to the rules I specified.

Note that Fowler's definition of a "service" layer isn't quite the same as my persistence service layer. My persistence services act as a bridge between application logic and low-level (per-table) persistence. Their methods all operate as independent database transactions, since - like I said - I consider it perilous to have the actual application code meddling directly with attached database objects. My service layer accepts detached objects, (re)attaches them, does application-specific persistence logic, then invokes the DAOs to do the grunt work then returns detached objects where appropriate. In a few cases, the service methods may be simple passthroughs, just to keep all application persistence interfaces all on the same level, but more often, there are multiple tables and inter-table logic considerations, so the service layer is anything but hollow.

My model app for this architecture actually builds out of 3 distinct JARs. One JAR has the DAOs and Entities in them, one JAR has the Persistence Services in them, and the final JAR is actually the WAR of the webapp itself, with the first 2 jars as libraries. It has the additional convenience that if I needed to got the full EJB route (which is quite possible for this particular app and its relatives), that the changes to the architecture would be fairly small.

The reason I have a separate JAR for the Persistence Services is because - as I said - these are services that are more or less specific to a single application. But in some cases it would be desirable to use them in other applications as well, so I isolate them both from the low-level general re-use components and the specific application using them.

Going back to the top, I discourage both active logic in DAOs and the indiscriminate use of database stored procedures. Stored Procedures are BAD because they lock you tightly to a particular DBMS product and because they tend to split application logic functionality across multiple layers, resulting in what I call a "treasure hunt" when it comes time to do maintenance. However, I'm not dogmatic about it. If there's a heavyweight function that can gain massive efficiency by running on the DBMS server, then a stored procedure is probably what I'll use. Likewise, I use constraints freely, and in the case of DAOs, I can enforce low-level data validation. Stuff that's tied to the database schema as opposed to a specific application.
 
Richard Grin
Greenhorn
Posts: 5
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thanks Tim for your detailed explanation.

However, even with this explanation I wouldn't say that putting business logic into the entities is very bad.

Your main reason is that it would reduce the ability to recycle these entities. But it is not frequent to use the same entities with such different business logics that it is a problem. On the other hand it is a often a problem to write the business logic outside of the entities because it is not in the spirit of object programming.

I am rather in the same spirit as Adam Bien: write the business logic in the entities except if the problems are too big. And it is always possible to refactor if the business logic in the entities is troublesome.

 
The only thing that kept the leeches off of me was this tiny ad:
New web page for Paul's Rocket Mass Heaters movies
https://coderanch.com/t/785239/web-page-Paul-Rocket-Mass
reply
    Bookmark Topic Watch Topic
  • New Topic