I'm building a MVC web application (using the Spring MVC framework), and I'm a little stumped on the best way to design a particular area.
The application has to interact with a series of web services which are not really all that greatly designed, and don't offer much abstraction in and of themselves - basically there is a web service method for each create/update/retrieve/delete operation for each "datatype", and there isn't much of an API beyond that. The web service client needs to know which methods to invoke, and in which order, to be able to create the data it needs to - in other words, there are no "transaction" based methods.
For example, simply to create a new user account requires calling a total of seven different web service methods to set up all of the records in the necessary tables (a user
record, adding the correct privileges
to that user, setting up the user's billing
details, etc).
I'm struggling with the best way to abstract this and encapsulate it within our application. Most of the app follows a standard flow:
request ---> Controller <---> Service/Business-level object <---> DAOs for data access
Within my application, I'm using my own set of "domain objects" to represent and abstract the datatypes defined in the web service WSDL, so that my domain logic is not dependent on the web service types and so that we can abstract and hide whichever details we like.
What I'm looking for some opinions on is the best way to design the "user creation process" I mentioned above as an example. The process to create a "regular user" involves calling seven different web services, as I mentioned, but this is just one "type" of user - we will have to be able to create several different types of users, each of which require different web services to be invoked.
Currently I've only designed this "regular user" creation, as a bit of a proof of concept - I have a domain object User
, a UserDao
interface which has methods for getUser(name)
and createUser(User)
, and a WebServiceUserDao
which implements the UserDao
methods and knows how to invoke the above-mentioned seven web service methods. The createUser()
method is called by a UserCreationService
, which is my business/service-level class, which in turn is invoked by the SignupController
.
But to expand this logic in order to be able to create the different user types (which are represented by different values in User.getType()
, I'm unsure where to draw the line between the business/service layer class and the DAO. For instance, should I:
- Create one
UserDao
implementation per "user type", so the logic to create each "user type" can be encapsulated in it's own class, and let theUserCreationService
decide whichUserDao
to use? This would correspond to 1 service class : many DAOs. - Should I break the
UserDao
into smaller pieces, one corresponding to each "record" that needs to be created in the web service / DB, even though my overall application doesn't need to know about each of these individual types? And then have differentUserCreationService
implementations for the various different "user types"? In other words, this strategy would have aPrivilegesDao
, aBillingPlanDao
, etc., even though I would have no need for a correspondingPrivilege
orBillingPlan
domain object. This would be many service classes : many DAOs. - Contain all of the logic for which web services need to be called for each "user type" in a single
WebServiceUserDao
? This would have the drawback of having a very complicated class (and PMD is already complaining about cyclomatic complexity), but all of this logic would be encapsulated in the one class and might lead to less complication when viewed from an overall API perspective.
One goal that I have with this application is to make sure that if we ever have to change the details of the data persistence, that all we need to do is change the DAO implementations - if we have to start interfacing with a different billing system, I don't want any part of the application to change other than at the DAO-level.
Any opinions? What kind of strategies do you use when deciding where to break down "business logic" versus "data access logic" when they seem to overlap?