|
WhySalve
Motivation behind Salve
Salve aims to fix the anemic domain model anti-pattern(1) (2) present in most modern java web-applications. With technologies like ORM we are able to model domain layer objects that contain application data, but we are unable to enrich them with behavior to act upon that data - the basic principle of OOP. The behavior is factored out into stateless singleton objects called services. This is done for the following reasons:
How Salve Fixes the ProblemThe ideal solution would allow domain layer objects to be lightweight, contain business logic, and have dependency injection that works under any lifecycle. One way to achieve this would be to allow the developer to specify dependencies as fields, and then rewrite the bytecode to remove those fields from the class and replacing access to them via a static lookup into whatever IOC container/dependency management system the application is using when the application is deployed. This is how Salve works. Lets take a look at a quick example: // This is a JPA entity
@javax.persistence.Entity public class Account {
// this is a dependency we wish Salve to instrument
// notice no need for @Transient because the field will be removed
@salve.Dependency
private EmailService email;
// this is database id of this account
@javax.persistence.Id
private Long id;
// this is a JPA field
private float balance;
// setters and getters
public void withdraw(float amount) {
if (amount>balance) {
email.sendOverdrawMessage(this);
}
balance-=amount;
}
}Without Salve such an object is not practical: there is no good way to set the EmailService dependency, and there is no good way to have Account be serializable. When the bytecode of Account class is processed by Salve, it will look like this: @javax.persistence.Entity public class Account {
// added by salve to facilitate lookup of email service dependency
private static final salve.depend.Key _salve_key$email=new salve.depend.Key(...);
// email field is removed
@javax.persistence.Id
private Long id;
private float balance;
// setters and getters
public void withdraw(float amount) {
// dependencies are looked up lazily and cached in a local var
// there is also a cache in salve that speeds up multiple repated lookups
EmailService _email=salve.depend.DependencyLibrary.lookup(_salve_key$email);
if (amount>balance) {
_email.sendOverdrawMessage(this);
}
balance-=amount;
}
}Lets take a look at the changes Salve made to the code:
For instructions on how to setup Salve in your project see the GettingStarted page |
Sign in to add a comment
Just a nitpick: The class name is misspelled. It should be "Account" instead of "Acount".
I think it's a bit presumptuous to call anemic domain model and "anti-pattern" - It's a pattern, sometimes it works, sometimes it doesn't. You may not like it, others do, like all patterns use them when they make sense but I find labeling it an anti-pattern discrediting to otherwise interesting work.
Interesting idea. However, anemic domain models exist and run in many extremely large systems. You still haven't proven that anemic models are "broken". You might want to spend sometime laying out a case against anemic models and then show how Salve can fix it.
You might also want to consider adding information about the pitfalls of Salve. The first one that popped into my mind was when a Salve object lands in a JVM without the appropriate service or access back to the grid/bus. It no longers provides a coherent contract via its public interface.
I'd also consider adding a discussion about alternatives and why you believe Salve is better. The one that stuck out in my mind as I was reading was the pattern of using non-serializable intelligent domain objects within a single JVM and providing a transformation to DTOs if the domain is passed outside that JVM.
Lastly, I would also suggest that you add information about the intended remote invocation pattern you are trying to solve. I would present a remote API and the corresponding client API and then show where Salve fits in.
Sorry, but I hate DTOs. The project I'm working on uses a bunch of them, and we're always running behind the facts of coordinating them with the regular domain classes. It's the kind of duplication I could live without.
Why would somebody use salve, instead @Configurable (or AutowireCapableBeanFactory?) annotation in spring?
Here is the link: http://debasishg.blogspot.com/2006/07/spring-20-aop-spruce-up-your-domain.html
@alex: spring solves only part of the problem. it doesn't solve serialization. coming from wicket, this is probably a major concern for igor..
@alex: see ComparisonWithSpringsConfigurable wiki page
Hello Igor. Any chance to integrate Salve with Spring's @Secured or JSR250 annotations (inside Spring)? I ran into frustration trying to create a rich domain model while securing some of its methods. @Configurable and @Secured just don't work together with AspectJ weaving.
@daniel: I just released 2.0 alpha1 which has an AOP framework. The support for @Transactional is already rebuilt on top of the AOP framework, I suppose you can easily do the same for @Secured.