This forum is now a read-only archive. All commenting, posting, registration services have been turned off. Those needing community support and/or wanting to ask questions should refer to the Tag/Forum map, and to http://spring.io/questions for a curated list of stackoverflow tags that Pivotal engineers, and the community, monitor.

Service + Web architecture questions

Mar 11th, 2009, 02:17 PM

Two Questions:

(1) In desinging a the service layer, I find myself hesitant exposing my domain objects to the user
- Why? Some domain objects, (ie. Lease) have an expiry date(or any other attrib, hashed password) set by the service (biz logic). When a user queries a Lease, he can now modify the domain object then persist with a different expiry date.
- I am hesitatnt to put in checks to make sure the date's haven't been tampered with. And if there is a lot of stuff,
- Should I
[a]: Only pass in String,Date,int... values into the service and not the objects? Bascially the values that need to change + the id of the Object.
[b]: Some new DTO type object, that has those biz logic determined fields declared as immutable.

(2) Package structure:
- Constv give me some good structure hints for the service Tier. But what about the Controller/View layers?
- com.company.web.controller is OK, but what is the most elegant way of organizing a package for a web app (spring web app)

1) It seems, the issue here is to achieve a proper degree of object granularity. You can have some domain classes whose instances will only be constructed within the service - using the objects passed from the client. So, for example, you may have to split your Lease object in two, with proper names, where one (only modified by the service in the middle-tier) would have additional fields and nest the other whose data can be safely set in the front-end and passed to the service.

2) About the package structure of a web application... I usually separate all classes into web-specific (UI related, MVC, webflow, servlets, etc.) and not web-specific. Then, just do whatever seems the most convenient and appropriate. Since, after you have extracted all your business components into separate functional modules, you are left with the web application as a single module, you can either group the classes by functionality - again, or by the type of work they do, e.g. validators, services, etc. There is no longer a danger of coupling unrelated components by making them all joint at some "persistence" package - because they all are integral part of the same application, anyway. if any distinct functional domains exist that are specific only to my web application, I usually create a namespace for each of them under com.mycompany.myapp.

Assuming you use the standard Maven structure, you may have something like this, under your Java folder...

Under Resources, you may have some of your properties files, templates, etc.

Under webapp, you may have your JS files, CSS files, image files (if your app does no use those from some other location on the server), some index.jsp that redirects to your dispatcher servlet, and WEB-INF. Your JSPs will be under WEB-INF, of course. You'll also have some config folder with your spring config XMLs. Group Spring configuration by functionality as well.
Something like that...

getExpirationDate() {
// return a copy of expirationDate to make sure the object is not changed.
}

I think ramoq meant that setting/modifying some data should only be controlled by the middle-tier service based on its internal business logic, not the client. So, just disallowing a property to be reset on a "lease" probably is not a good idea - especially, because that would mean hardcoding some very specific requirement into the "lease" concept. It is reasonable - and logical - to assume that an expiration date may be set, and then re-set on a lease, in general. So, the question is: who and when should be able to do it? It is up to the use case that defines the context in which the "lease" is used. The service in this case implements the use case that governs that particular requirement. This is a good example of how the domain entity/service model can be a natural (easy and intuitive, and more logical) alternative to the DDD approach from the Evans book - per our conversation in another thread. Such specifics should never be part of the object that defines the entity. I hope this somewhat illustrates the reasoning behind the points I was making.

Comment

I think ramoq meant that setting/modifying some data should only be controlled by the middle-tier service based on its internal business logic, not the client. So, just disallowing a property to be reset on a "lease" probably is not a good idea - especially, because that would mean hardcoding some very specific requirement into the "lease" concept. It is reasonable - and logical - to assume that an expiration date may be set, and then re-set on a lease, in general. So, the question is: who and when should be able to do it? It is up to the use case that defines the context in which the "lease" is used. The service in this case implements the use case that governs that particular requirement. This is a good example of how the domain entity/service model can be a natural (easy and intuitive, and more logical) alternative to the DDD approach from the Evans book - per our conversation in another thread. Such specifics should never be part of the object that defines the entity. I hope this somewhat illustrates the reasoning behind the points I was making.

If there is not a rule (like only one Expiration Date change), it is not a business logic, is an infrastructure concern and you can take other solution to resolve it even in DDD (you are not forced to put everything into the objects).

Comment

So, for example, you may have to split your Lease object in two, with proper names, where one (only modified by the service in the middle-tier) would have additional fields and nest the other whose data can be safely set in the front-end and passed to the service.

Constv, could you provide a basic example of this?

Comment

Could you please tell me what your current object looks like (what properties it has), and the handling of which properties you want to restrict to the middle tier. It would be easier to come up with a meaningful example. Thanks.

Comment

An option would be to create a security aspect to restrict the access to the public setters of the property (and also change the getters to return copies).

EDIT: Coincidentally I was reading a blog today an I read a post talking about internal DTOs and about hiding business methods from the view. You could be interest in reading it. (take a look at Using it to Forbid Calls to Dangerous Methods).

Comment

Ok... It sounds like you want your application [middle tier] to have control over the lease "finalization" - after the user provides some information. In other words, the users are expected to supply some necessary data, but then, it is up to your business component to process that data and decide what the rest of the lease attributes should be. Am I interpreting it correctly?

Since all your business logic and persistence happens in the middle tier that is only accessible to the client via the public API (as it should be), the only proper way to do what you want to do is to correctly design your public API on your [middle-tier] business object/service. Since there are no legitimate use cases for your clients to fully construct leases by themselves providing things like expiration dates, or other critical data that should be derived in the MT, your business object must not provide any API that might allow them to do that. As simple as that. A concept of a "lease" implies an expiration date. Therefore, a reasonably implemented Lease object should always provide a way - for anyone who needs to - to assign such date on it. Period. So it would be completely inappropriate to create some restrictions on the object - or around it on the client - to set any parameters on them. If setting the expiration date on each lease instance is the responsibility of a middle-tier business object (single instance managed by Spring and re-usable by multiple threads) that has the knowledge and ability to do so, then let that object do that. Control what clients can ask for by the available operations (public API) on that object. If any client wants to create some loose instance of a Lease class and set the date on it, fine, who cares? All that matters is that such client can never put that [invalid] object anywhere inside your system. They won't be able to do that since the only service that manages Leases, simply does not provide an API for it. It is a textbook case for separation of actors from scenarios they may participate in.

You still would want to have a couple of distinct domain classes that you would use for different purposes. The Lease class will represent your full "finalized" car lease. And you could have a separate LeaseApplication(?) class whose instances would be populated in the front-end and passed to your LeaseService business object. The service would extract the necessary data from the LeaseApplication instance passed to it, perform any necessary operations in the middle-tier, like calculate the appropriate expiration date, or whatever, construct the Lease object and possibly pass it to the DAO to save or/and return the new Lease object to the client to display the Lease summary. There will be NO way for the client to pass the full Lease object to the middle tier to be persisted or used in any way without the service having a full control over it. Unless at some point your requirements change (for example, you will add the functionality to allow users with admin privileges to log in and manually change dates on the existing leases.)

HTH

Comment

Two ideas:
1) If you want some fields to not be updateable, but ok to be viewed then annotate the field with @Column(updateable=false)
2) If you want different fields or some subset to be displayed then you need to have a different object as the forrm backing bean and map to the domain object. You can create a reflective utility to automate the mappings.

Yes, this ("main/test", "src/resources/webapp" approach) is actually the de facto standard for the directory structure for modules - originally introduced by the Maven project. Except , instead of your "core" and "web" modules you should have any reusable modules with the functionality that should not be built into your [web] application but rather externalized. In other words, each module - in addition to your web application module - should represent a distinct functional domain, not just some core, UI, or persistence. Each such module should define its own model. service, and persistence classes, which would allow it to be fully independent and distributable with various applications, if necessary. Among these "external" modules there normally would be some kind of "commons" or "core" module that contains common interfaces, classes and utilities that may be used by any other modules. Each specific application is just another separate module in the project, and it will use other modules as dependencies (e.g. packaged as jars in the web app's war file.) This also allows you to have different project configurations, e.g. loading sources for some modules and using the others as jar dependencies, while other developers are working on updates to those modules. You will avoid the necessity of having a huge monolithic project with thousands of classes where nothing can be taken out and worked on independently. (That happens all the time!) I have tried to explain all that in some of my earlier posts.

Your controllers and other web-app related java classes must live under the web application's modu;e's java/src/main tree. Also, you got it wrong with the contents of the resources folder of your webapp.

-----target (build artifacts such as .war file will be placed here...)

I would highly recommend to follow these guidelines, and, most importantly, to use Maven for your project/build configuration and management. It is a very powerful and flexible tool. It is 100% IDE agnostic, and all major IDEs fully support Maven. This means, you will be able to install a Maven-configured project in any such IDE in minutes by pointing the IDE to the Maven configuration files (POMs) instead of using the IDE-specific project settings. Take a look at the Maven site, it is worth spending some time getting familiar with Maven.