Friday, June 29, 2007

Workflow engine integration, where does it go?

In one of the projects I have worked on, OSWorkflow is used to manage workflows. It is integrated to the service layer instead of to the domain objects.

The design feels unnatural somehow:

  • The workflow id is stored in the domain object, passed as a parameter in constructor. However, it is not used by the domain object itself. The domain class provides a getter method so that the service layer can retrieve the workflow id and load the workflow instance from OSWorkflow.
  • After loading the domain object and then the workflow instance, the service layer needs to pass to the workflow engine some properties from the domain object and some properties from the DTO that carries the input of the user for the current request, so that the workflow engine can work out what is the next status for the domain object.
  • The domain class also expose a method to allow arbitrary update of the 'status' field so that the service layer can update the domain object with the status that is come up with by the workflow engine. The domain model loses encapsulation.
  • The service layer becomes thick while the domain model becomes anaemic...

IMHO, this design smells.

The change of state in a domain object should be managed by the domain itself. Any application requirements, such as sending email notifications, can be implemented as state change event listeners.

If the workflow logic gets too complicated, workflow engines like OSWorkflow come to rescue. But even if the workflow logic is delegated or outsourced to a workflow engine, it is still domain logic; thus, the workflow engine should be integrated directly into the domain objects instead of to the service layer.

In order for domain objects to work with a workflow engine, domain objects need to have a reference to the workflow engine, which used to be an issue.

With domain object dependency injection feature introduced in Spring 2, this problem is solved. Even if you are using Spring 1.2 or other IoC Containers that do not support domain object dependency injection, or if you are not using an IoC container at all, you can still use Registry pattern where a domain object can look up a dependency.

In conclusion, state change is part of the domain logic, and even if it is outsourced to a workflow engine, the workflow engine integration should still happen in the domain object, without leaking the implementation details to other layers. And technically, there is no more barrier that prevents injecting the workflow engine into domain objects.

Sunday, June 24, 2007

Is using domain entities in presentation layer encouraged?

There is an interesting discussion going on in the Spring support forum about using Hibernate entities in web layer.

In his reply, Debasish said:
I am all for using domain entities in the presentation layer. If you read the Expert Spring MVC book or the Hibernate book, both of them encourage using smart domain models and reusing domain objects in the presentation layer. The Spring MVC book recommends using domain objects as command objects and the Hibernate book recommends doing away with the behaviorless JSF backing beans in favor of smart POJOs in SEAM.
I have plenty of respects for Debasish. However, I have to disagree on this.

One thing that I have kept in mind is that Spring MVC and Hibernate's development started in the dark age of EJB 2.0, which forced developers to build enterprise applications with anaemic domain models using transaction scripts. At that time, many people equalled Java Beans to POJOs and domain classes. Unfortunately that was misleading... In a rich domain model, typically entity classes are NOT Java beans, because providing setter methods can easily break invariants of a domain class. Since then, Domain Driven Design has gained wider adoption and recognition.

In a recent presentation "Are We There Yet?", Rod Johnson, the founder of Spring, asked the attendees a question: when working with Hibernate, who use property access and who use field access? Those who use property access are persisting DTOs, or, using an anaemic domain model. I cannot recall the exact words, but that's basically what he said and meant.

I would like to add: that those who use domain classes as command objects in the web layer are working with an anaemic domain model.

For example, in one of the projects I have worked on, there is a Fraud class, which has three fields: fraudPaymentDate, fraudPaymentAmount, amountRecovered and dateRecovered. The invariants regarding these three fields are:
1) amountRecovered must not exceed fraudPaymentAmount.
2) When amountRecovered is $0.00, dateRecovered must be null.
3) When amount Recovered is greater than $0.00, dateRecovered must be present, in the past and must not predate fraudPaymentDate.

If this class provides setter methods for all these fields and is directly used as a command object, there is no way not to break invariants in the binding phase of Spring MVC:
If setAmountRecovered() is invoked with a non-zero amount before setDateRecovered(), then invariant no. 3 is broken.
If setDateRecovered() is invoked before setAmountRecovered(), then invariant no. 2 is broken.

Therefore, a command object typically does not maintain data integrity. Usually a validate() method is provided to the client to ensure data integrity AFTER the binding.

There is one big drawback with using validate() method: the domain object relies on its client to call its validate() method to check data integrity or on a trigger from an event such as save(). However, when using Hibernate, domain objects can be persisted transparently without the domain object being persisted beware of the fact that it's being persisted. Thus, a corrupted domain object can easily be persisted to database.

When using a rich domain model with DTO, this problem is solved:
The DTO can be used as a command object in the web layer and passed to the service tier. The UpdateService can construct two value objects fraudPaymentInfo, which contains both fraudPaymentDate and fraudPaymentAmount, and recoveryInfo, which contains both amountRecovered and dateRecovered, according to the data in the DTO and invoke the Fraud object's updatePaymentInfo() and updateRecoveryInfo(). Invariants are always maintained in the process.

Monday, June 18, 2007

Domain Classes should not be passed to UI layer.

Debasish Ghosh, one of my favourite bloggers, posted an entry titled
Domain Classes or Interfaces? last October. Later he posted a follow-up entry Abstract Classes or Aspect-Powered Interfaces?.

He had this great debate with Sergio Bossa in the mailing list of Domain Driven Design (DDD).

I was surprised to read that both Debasish and Sergio are in favour of passing domain objects to the UI layer and using them as command objects, in the context of DDD discussion, because this kind of usage is code smell of Anaemic Domain Model.

As I understand, one major reason cited by Debasish to use abstract domain classes is to maintain the integrity of domain objects, making sure no constraints are violated.

However, when a domain class is designed following the philosophy of DDD, there are typically few setter methods, making them unsuitable to be used as a command object in the UI layer.

Moreover, there are typically some business methods that you would not like to be exposed to the UI layer, such as those that access database and must be wrapped in a transaction, making it even more undesirable to pass domain objects to the UI layer.

In my opinion, a domain class typically has three interfaces (not necessarily Java interfaces):
* Business interface - these method usually bear domain-specific meaning in their names.
* State Exposure interface - typically getter methods
* State Manipulation interface - these methods update the object state without violating any constraints, and are usually named updateXXX instead of setter.

Business interface should not be exposed to UI layer. If the return value of any of the business methods are to be displayed in a UI, a getter method should be defined in the State Exposure interface, which should then delegate to the business method.

State Exposure interface is usually passed to UI layer for display. In a proper application design, the role to expose domain object states is usually assumed by a Data Transfer Object (DTO). In a Spring+Hibernate application, this Java interface can be implemented by the domain class directly, and the domain objects can be passed to the UI layer without exposing business methods. Typically, Open Session In View (OSIV) is required in this usage.

Even if the domain class implements the State Exposure interface , the domain objects that are passed to the UI layer should not be used as command objects. Each piece of update should be invoked on a facade, which then invokes methods defined in the State Manipulation interface. The piece of information for update is usually passed in as a DTO.

Note that there is difference between the DTO for state exposure and the DTO for state manipulation:
The DTO for state exposure usually captures most, if not all, state of the domain object.
The DTO for state manipulation usually captures only the piece of state that is being updated.

In conclusion, because of the three interfaces a domain object usually has, it is strongly discouraged to pass domain objects to UI layer or use them as command objects, when applying DDD.

Sunday, June 10, 2007

Paypal, You Did It Again!

This is a follow-up to my previous blog about Paypal's pathetic service.

I was able to log in eventually. However, since then, I received two warning email from Paypal, requesting me to accept their Policy Updates to prevent Account Limitation.

The first email is like this:
Dear ***,

PayPal's records indicate that you have not accepted the Product Disclosure Statement.

Failure to accept the Product Disclosure Statement within thirty days will result in access to your PayPal account being limited. If your account is limited, you will no longer be able to receive or send payments.

PayPal values you as a customer and does not want you to lose the valuable benefits of your account. Please visit the PayPal website to accept the Product Disclosure Statement. To do this, copy and paste the following URL into your browser: https://www.paypal.com. Then, log in to your account and click the New Policy Update link on your Account Overview page.

Thank you for using PayPal!
The PayPal Team

----------------------------------------------------------------

PayPal, an eBay company

Copyright © 1999-2007 PayPal, Inc. All rights reserved.

PayPal Australia Pty Limited ABN 93 111 195 389 (AFSL 304962). Any general financial product advice provided in this site has not taken into account your objectives, financial situations or needs.
PayPal Email ID PP 878

I went to their website and visited their "Policy Updates" page after log in. However , I could not find any input methods to allow to indicate whether I would accept or decline the policies, as shown in the following screens (the top and the bottom of the page):

A few days ago, I received the final warning email from Paypal:
Dear ***,

Recently, we sent an email notice to remind you to accept the updated Product Disclosure Statement within thirty days to avoid restrictions being placed on your account.

You now have seven days to accept the policy. If your account is limited, you will be unable to send or receive money, but will be able to withdraw any remaining balances.

PayPal values you as a customer and does not want you to lose the valuable benefits of your account. Please visit the PayPal website to accept the Product Disclosure Statement. To do this, copy and paste the following URL into your browser: https://www.paypal.com. Then, log in to your account and click the New Policy Update link on your Account Overview page.


----------------------------------------------------------------

Thank you for using PayPal!
The PayPal Team

----------------------------------------------------------------

PayPal, an eBay company

Copyright © 1999-2007 PayPal, Inc. All rights reserved.

PayPal Australia Pty Limited ABN 93 111 195 389 (AFSL 304962). Any general financial product advice provided in this site has not taken into account your objectives, financial situations or needs.
PayPal Email ID PP 879
I logged in to Paypal again and still couldn't find the "Accept" button or checkbox that I expected.

Today, I received the notification of limitation on my account:
Dear ***,

We regret to inform you that your account is now limited because you failed to accept one or more of the PayPal updated policies. Over the past month, we have contacted you by email twice asking you to accept the updated policies within 30 days to avoid account limitation.

Because your account is limited, you cannot send or receive money. Access the link below to learn how to withdraw any remaining balances from your account should you decline PayPal's updated policies.

Please visit the PayPal website to accept the policy updates and restore your account status. To do this, copy and paste the following URL into your browser: https://www.paypal.com. Then, log in to your account and click the New Policy Update link on your Account Overview page.

We encourage you to read the new User Agreement and Privacy Policy, as they contain important information about your PayPal account, your rights as a PayPal user, and the ways in which PayPal will use your personal information.

To view the Product Disclosure Agreement, please visit the PayPal website. To do this, copy and paste the following URL into your browser: https://www.paypal.com. Then, log in to your account and click the New Policy Update link on your Account Overview page.

Sincerely,
PayPal


-----------------------------------
Additional Information
-----------------------------------

Q. Why will my account be limited if I do not accept the policy updates within 30 days?

A. By agreeing to the terms of PayPal's updated policies, you are entering a binding, voluntary contract with PayPal. Therefore, if you decline the updated policies, we must assume that you no longer agree to abide by those rules, and your account will be limited. When your account is limited, you cannot send or receive money until you accept each policy update required. You will also have the right to withdraw any remaining balances if your account is limited.

Sincerely,
PayPal


----------------------------------------------------------------

PayPal, an eBay company

Copyright © 1999-2007 PayPal, Inc. All rights reserved.

PayPal Australia Pty Limited ABN 93 111 195 389 (AFSL 304962). Any general financial product advice provided in this site has not taken into account your objectives, financial situations or needs.
PayPal Email ID PP178

I logged in again and couldn't find out how I can accept the policies.

For God's sake, haven't they ever tested their website to make sure a user can accept their policies?

So so so pathetic... And I really don't want to have any business with them any more. Unfortunately, I have to... Yahoo!'s Flickr only accepts Paypal payments... But I'll try to stay away from Paypal as possible as I can. I just don't trust them: if they can't even make their website decently functioning, how can I trust them to abide by privacy laws or safeguard credit card information...