Demonstrating GOOS for a web application

222 views
Skip to first unread message

Vincent Tencé

unread,
Oct 11, 2011, 1:11:34 PM10/11/11
to growing-object-o...@googlegroups.com
Hi all,

Together with some friends at work, I've built a web app to apply the book concepts to the web. I'm sharing it in hope in can be useful and also to get feedback to improve the code. 

The application follows the ports and adapters pattern and uses the following for the test infrastructure:

* System tests use weblicker (window licker and web driver) and an in-process jetty server to deploy the application
* Unit tests of web controllers are done with regular junit tests using jmock2
* Unit tests of the velocity views use junit tests with a set of custom hamcrest matchers and a CSS3 selector library
* Unit tests of domain objects use regular junit tests with a set of custom matchers for the bean validation API
* Integration tests with the database are done with junit tests. They exercise transaction boundaries and database migrations.

For the technology stack, we did *not* chose what we thought were the best possible frameworks or tools, rather common open-source tooling that many teams are struggling with ;-). We had to balance with needs for testability, of course. It was painful at times, but fun as well :-) We developed several custom tools to make testing at all levels possible, which is btw one of the key takeaways. The application uses Spring MVC for the web framework, Velocity and Sitemesh for view rendering, and Spring Core, Hibernate and Maven.

Interesting to note, test coverage is around 99%, simply achieved by being religious about not writing any code (java, sql, annotation, html, xml, etc.) without having a failing test first. The remaining 1% is code that improves tests diagnostics and is covered when tests fail. That code was driven when unit tests where still red.

The Maven build was probably the biggest pain (my maven was kind of rusted). Unfortunately, I've got no doc on the build but if there is interest, I will write basic instructions for running the application and tests.

The code for the app : https://github.com/testinfected/petstore
The code for the custom hamcrest matchers: https://github.com/testinfected/hamcrest-matchers

Looking forward for feedback of any kind

Cheers,
-- Vincent

David Peterson

unread,
Oct 13, 2011, 4:41:06 AM10/13/11
to growing-object-o...@googlegroups.com
Thanks for sharing this code Vincent. What kind of feedback are you after?

David

Vincent Tencé

unread,
Oct 13, 2011, 9:54:33 PM10/13/11
to growing-object-o...@googlegroups.com
Hi David,

I'm interested in anything you'd be willing to share ;-)

More specifically:

- How could I make the example more useful?
- What's missing? (for instance, I plan to add javascript tests and maybe a simple swing admin application)
- What you would have done differently and why?

Cheers,
-- Vincent

2011/10/11 Vincent Tencé <vte...@gmail.com>

David Peterson

unread,
Oct 15, 2011, 5:39:51 AM10/15/11
to growing-object-o...@googlegroups.com
Hi Vincent,

The code looks really clean and easy to understand. I like the way you have made a clear separation of the domain classes and interfaces from the specifics of the implementation.

I'm not sure whether there still might be some domain logic in the controllers that could be moved into the domain. For example, the PurchasesController (covered by the PurchasesControllerTest) calls a CheckoutAssistant to generate an Order from a Cart and then calls the PaymentCollector to collect payment on the order. Perhaps instead of using the controller to manage the communication between these objects, the objects could talk to each other and then callback the controller?

Another suggestion, while I'm talking about that particular test, is that in one test method you have:

final CreditCardDetails incompleteDetails = aVisa().build();

I tend to make my builders create valid objects when no parameters have been specified (i.e. in the builder I have some reasonable defaults for all the parameters, which can be overridden as necessary) but I guess that's a matter of taste.

In the other test method you have:

final CreditCardDetails paymentDetails = aVisa().
                billedTo(anAddress().
                    withFirstName("John").
                    withLastName("Leclair").
                    withEmail("jlec...@gmail.com")).
                withNumber("9999 9999 9999").
                withExpiryDate("12/12").build();

In this case, I believe that these are supposed to be valid credit card details, so you might name that variable "validCreditCardDetails" to make it clear. You might also pull this out into a private method (because the names, expiry dates, numbers etc. are kind of irrelevant to the test), or, even better, create static methods on the builder: validCreditCardDetails() and invalidCreditCardDetails() so that other tests can use them, if necessary.

Has anyone else on the group had a chance to look at the code? It's nice code and good to see the GOOS approach applied in a familiar domain. I'd be interested to hear what others think. 

Thanks again for sharing it, Vincent.

Warm regards,
David

Christopher Gardner

unread,
Oct 15, 2011, 11:18:26 AM10/15/11
to growing-object-o...@googlegroups.com
Accolades to Vincent et al for putting this together. At risk of
merging two GOOS threads, DDD, and some Uncle Bob points on
architecture, here are some thoughts:

* The domain model does have a number of getters and setters. They
make it easy to hang more and more dependencies onto their classes.
Are there options to reduce these?

* I agree with David's point about moving more Controller-managed
communication to the domain objects themselves.

* Uncle Bob has recently been talking about Jacobson's use case
controllers, calling them Interactors. If' I'm not mistaken, these
Interactors correspond to DDD's notion of an application layer. Of
course this application layer should at most be a simple coordinator
of domain objects and know nothing about specific domain logic itself.
This layer lets you use any kind of front end (a competing web
product, web services, command line, thick client, etc.).

* I believe Uncle Bob suggests Interactors should communicate in terms
of data structures and exchange domain objects. Does this relate in
any way to Nat's recent comments about using meta-mapping at the
boundary level?

So I would ask

* How can the getters and setters be reduced or eliminated (an oft
repeated question)?
* Is there a need for some type of coordination (application,
interactor, etc.) layer?
* Should Spring MVC know anything about real domain objects?
* Would it be useful to use something like MVP with Spring MVC at the View?

Vincent Tencé

unread,
Oct 15, 2011, 3:23:23 PM10/15/11
to growing-object-o...@googlegroups.com
Thanks David for the feedback. It's much appreciated.

I'm not sure whether there still might be some domain logic in the controllers that could be moved into the domain. For example, the PurchasesController (covered by the PurchasesControllerTest) calls a CheckoutAssistant to generate an Order from a Cart and then calls the PaymentCollector to collect payment on the order. Perhaps instead of using the controller to manage the communication between these objects, the objects could talk to each other and then callback the controller?

I agree. There is certainly a better way. I will consider introducing another domain object which hides the purchase flow.
 

In this case, I believe that these are supposed to be valid credit card details, so you might name that variable "validCreditCardDetails" to make it clear. You might also pull this out into a private method (because the names, expiry dates, numbers etc. are kind of irrelevant to the test), or, even better, create static methods on the builder: validCreditCardDetails() and invalidCreditCardDetails() so that other tests can use them, if necessary.

Agreed. Seems so obvious now ;-) 

-- Vincent

Christopher Gardner

unread,
Oct 15, 2011, 3:30:08 PM10/15/11
to growing-object-o...@googlegroups.com
A correction below.


> * I believe Uncle Bob suggests Interactors should communicate in terms

> of data structures and /_not_/ exchange domain objects.  Does this relate in


> any way to Nat's recent comments about using meta-mapping at the
> boundary level?

Vincent Tencé

unread,
Oct 15, 2011, 3:37:33 PM10/15/11
to growing-object-o...@googlegroups.com
Hi Christopher,

Accolades to Vincent et al for putting this together.  

Thanks


* The domain model does have a number of getters and setters.   They
make it easy to hang more and more dependencies onto their classes.
Are there options to reduce these?

I'd like to see them reduced as well, but atm those getters are used by the velocity view. I don't have any other satisfying option.

A suggestion, anyone?

* Uncle Bob has recently been talking about Jacobson's use case
controllers, calling them Interactors.  If' I'm not mistaken, these
Interactors correspond to DDD's notion of an application layer.  Of
course this application layer should at most be a simple coordinator
of domain objects and know nothing about specific domain logic itself.
 This layer lets you use any kind of front end (a competing web
product, web services, command line, thick client, etc.).

Sounds like something to try. As you said, the DDD application layer is missing.

* Should Spring MVC know anything about real domain objects?

There's a tradeoff here I think. If like the simplicity of declaring validations on the domain objects and letting spring mvc and hibernate
use those declarations to keep the code DRY. What do others think?

-- Vincent

Vincent Tencé

unread,
Oct 15, 2011, 3:55:48 PM10/15/11
to growing-object-o...@googlegroups.com
I'm interested in feedback on the system tests. As you'll see in the code, there are 3 layers of abstractions in the system tests :

1. The junit tests describe the functionality
2. The petstore driver hides the user activities (e.g. navigation flow) details
3. The page drivers hide the UI interaction details (clicking, entering text, selecting from a combo. etc)

The page drivers are similar to page objects (http://code.google.com/p/selenium/wiki/PageObjects), *but* are responsible for making the assertions (which is the opposite of what the pattern suggests). I've read people advocating against page objects and recommanding using a level of abstraction that is user activities oriented instead. From experience I think that both levels are needed, the user (navigation) level of abstraction, and the page one. Thoughts?

-- Vincent

2011/10/13 Vincent Tencé <vte...@gmail.com>

David Peterson

unread,
Oct 15, 2011, 4:51:30 PM10/15/11
to growing-object-o...@googlegroups.com
I agree with you; it's worth having multiple levels of abstraction down to the page-driving or component-driving level. It makes the drivers so much simpler and easier to reuse in other tests.

In general, I try to make everything intention-oriented rather than activity-oriented (especially at the higher levels). E.g. a system test may need an authenticated user, but doesn't need to know anything about the activities involved in authenticating a user.

David

Christopher Gardner

unread,
Oct 16, 2011, 10:29:20 AM10/16/11
to growing-object-o...@googlegroups.com
I noticed the domain model has JPA entities, as well. A thing with
which I've been grappling is the pragmatism of applying JPA
annotations v. returning to the days of yesteryear by moving metadata
to external files or alternate mechanisms. JPA annotations are quite
verbose. Now what happens if you change the storage mechanism with
annotation support? Do you rip out the old and replace the new
annotations? Do the new and old annotations live side by side? This
is definitely not a criticism of the petstore: It's my own conflict
with the trend of Java annotation proliferation.

Vincent Tencé

unread,
Oct 16, 2011, 8:14:55 PM10/16/11
to growing-object-o...@googlegroups.com

2011/10/16 Christopher Gardner <chris.r...@gmail.com>

I noticed the domain model has JPA entities, as well.  A thing with
which I've been grappling is the pragmatism of applying JPA
annotations v. returning to the days of yesteryear by moving metadata
to external files or alternate mechanisms.  JPA annotations are quite
verbose.  
 
I agree, they get verbose pretty quick. On the other hand, I've not used XML configuration files for
Hibernate since 2005 I think. So I hardly remember what it's like :-)
 
Now what happens if you change the storage mechanism with
annotation support?  Do you rip out the old and replace the new
annotations?  

I would probably do (thank god we have db integration tests!). If you had configuration files, I guess you would get rid of them and start anew.
 
Do the new and old annotations live side by side?  This
is definitely not a criticism of the petstore: It's my own conflict
with the trend of Java annotation proliferation.

I'm not found of annotations too. The dilemma is that I'm not found of lengthy XML files either. 

-- Vincent 

Josue Barbosa dos Santos

unread,
Oct 17, 2011, 7:48:11 AM10/17/11
to growing-object-o...@googlegroups.com
> I'm not found of annotations too. The dilemma is that I'm not found of
> lengthy XML files either.

In Spring Roo they used aspects to solve this. So your class doesn´t
have the JPA annotations, but they used aspects to construct an
annotated class. In fact, Spring Roo prefers to use aspects do
construct many things. One aspect to toString(), one aspect to JPA
annotations etc. It is in this way because Spring Roo try to be a live
framework. So when you create a new field in your class, the framework
automatically change de JPA aspect, the toString() aspect, the view
etc.

I am not saying that it is the right approach. Just showing what others did.

--
Abraços,
Josué
http://twitter.com/josuesantos

Daniel Wellman

unread,
Oct 17, 2011, 10:48:20 AM10/17/11
to Growing Object-Oriented Software
On Oct 15, 2:37 pm, Vincent Tencé <vte...@gmail.com> wrote:
> * Should Spring MVC know anything about real domain objects?
>
> There's a tradeoff here I think. If like the simplicity of declaring
> validations on the domain objects and letting spring mvc and hibernate
> use those declarations to keep the code DRY. What do others think?

I find myself going back and forth on this one.

On the one hand, I think that letting Spring handle validations can
lead to succinct controller code. That is, if you use the right
controller type and framework machinery, you need only write the
"happy path" (all valid) in the controller, and the framework handles
the invalid scenarios for you automatically. Plus it also seems
pretty easy to collect all the validation errors at once - e.g. "First
Name, Last Name, and E-mail are required". If I were created an
object that wouldn't be created by the UI framework (e.g.
programmatically) then I would want to protect the object from
entering an invalid state by throwing IllegalArgumentExceptions in the
constructor or possibly setters. It seems like this strategy doesn't
make it easy to collect all invalid fiels at once -- Spring seems
prefer setting all the fields on the object, then collecting the
errors one at a time.

On the other hand, using this "set all the fields at once and let the
framework validate it" means that object can be created in an invalid
state - especially if you create it in other places outside the UI.
So then I find myself having to reason about if this object could have
been created correctly or not. And in general once I'm in the domain
code, I want to just assume that the object has been created correctly
- and "valid" might mean something different in that context. When
taking in data from a form, "valid" can mean "The user entered all the
required fields", but then in the domain, "valid" isn't always
specific enough - e.g. "this order can be shipped".

Which means I'm wondering about creating objects that use the
validation framework for UI entry, but then translating them in to
different objects once they pass the UI layer. But I could imagine
this leading to two objects with similar names - a Phone Number object
for the UI, and a Phone Number object for the domain. And having two
of everything feels like it could lead to an annoying explosion of
classes - but I haven't fully tried this approach, so maybe it's not a
problem.

What about everyone else?

Dan

Christopher Gardner

unread,
Oct 17, 2011, 10:52:08 AM10/17/11
to growing-object-o...@googlegroups.com
If you take an MVP approach, perhaps this data could be represented as
a data structure in the view, pre-validated, then sent to an
application layer where it is used to create domain objects.

Lance Walton

unread,
Oct 17, 2011, 12:23:59 PM10/17/11
to growing-object-o...@googlegroups.com, Growing Object-Oriented Software
In PLoP 4, Carlson describes the Essence Pattern:

Make your domain model object, Foo, immutable. Give it a low visibility constructor that takes a FooEssence. In Java, package visibility does it. In C++, make it a friend.

The FooEssence is mutable and has a factory method that validates the state and then constructs a Foo, passing itself (memento like) to represent the required state.

Might help.

Regards,

Lance

Christopher Gardner

unread,
Oct 17, 2011, 1:59:44 PM10/17/11
to growing-object-o...@googlegroups.com
Nice. Looks similar Bloch's application of the GoF Builder and even
like Nat and Steve's test data builders.

Lance Walton

unread,
Oct 17, 2011, 3:09:38 PM10/17/11
to growing-object-o...@googlegroups.com
Actually, if I recall correctly, the FooEssence's factory method just invokes Foo's constructor and Foo's constructor asks the essence to validate itself before using FooEssence's state. Whatever, you get the idea.
Reply all
Reply to author
Forward
0 new messages