11
\$\begingroup\$

I would like to solicit advice on everyone's thoughts on how best to combat the Anemic Domain Model anti-pattern when building out a system based on web services.

One of our goals is to build a set of core web services that expose the most basic services we reuse repeatedly in our organization, which is the creating of domain models. Right now we have a small library that we share and reuse but as we grow our team it would be much nicer to centralize these basic services. Over time our systems are going to change as some of the data may come from the cloud (Salesforce.com or AWS) so we're not just isolating basic DAO code in a web service but also application integration.

For example, our customer data comes from the accounting, CRM, and order processing systems. Configuration is a real pain because every app that ships needs to be bundled with the core library and configuration on each system. I would like to centralize the creation of models, ala, SOA, but retain a rich model higher up in the Service Layer / Facade.

If you think in general that this is a bad I'd be interested in hearing why!

My thought is to define a domain object Employee that has an EmployeeService injected. At runtime the EmployeeService implementation is an EmployeeWebServiceClientImpl that implements said interface. EmployeeWebServiceClientImpl uses a web service proxy to the server.

On the server-side of the web service we have EmployeeWebService invoking EmployeeDao to query the database. Could just as easily be a class calling out to Salesforce.com to get data. We would share a library that contained the domain model and interface so you would deserialize the web service response directly into a class that contained the needed business logic.

Below is some example code in order from client to server:

//Example of client
public static void main(String[] args) {
    try {
        Employee employee = Employee.getEmployee("james");
        if (employee.isEligibleForRaise()) {
            System.out.println("Give that man a raise!");
        }
    } catch (RuntimeException e) {
        System.out.println("Oh no!");
    }
}


//Employee Domain Object
public class Employee {

  private String name;

  private String username;

  private static EmployeeService service;

  public static Employee getEmployee(String username) {
    return service.getEmployee(username);
  }

  public static List<Employee> getAllEmployees() {
    return service.getAllEmployees();
  }

  public boolean isEmployeeEligibleForRaise() {
    //business logic here
    return true;
  }


//Getters & Setters

 ...
}

//EmployeeWebServiceClientImpl
public class EmployeeWebServiceClientImpl implements EmployeeService {

  //A client web service proxy to our core basic services
  BaseWebServiceProxy proxy;

  @Override
  public Employee getEmployee(String username) {
    return proxy.getEmployee(username);
  }

  @Override
  public List<Employee> getAllEmployees() {
    return proxy.getAllEmployees();
  }
}


//On the server-side we have EmployeeWebService
public class EmployeeWebService implements EmployeeService {

  EmployeeDao employeeDao;

  @Override
  public List<Employee> getAllEmployees() {
    return employeeDao.getAllEmployees();
  }

  @Override
  public Employee getEmployee(String username) {
    return employeeDao.getEmployee(username);
  }
}

Is that making sense? Basically the plan is to keep core business logic in the Employee domain object but isolate the data access logic in a web service.

Thoughts?

\$\endgroup\$
3
\$\begingroup\$

You're adding a lot of complexity here. Plus, you're going against the point of a Service Layer and Domain Model. Your Domain Model should probably use either Active Record or Data Mapper.

The point of a Service Layer is to act as an end point (API) that holds common business logic that deals with integration of domain model objects. Your service layer should just query repositories and delegate calls to process business logic to your domain model (which should hold as much business logic as possible). Injecting it into your domain model adds persistence concerns, which it should not care about.

|improve this answer|||||
\$\endgroup\$
  • \$\begingroup\$ Am only talking about the two inner-most circles of ServiceLayer: Domain Model and Datasource. My example above is trivial. In practice my model is called from SL/Facade. I had ActiveRecord in mind but it is not as simple as the example on Martin's site. We rarely talk directly to a db. Our data is behind vendor API's (CRM, HR, Project Mgmt etc..), ActiveDirectory, & DB. To create our model we talk to 6-7 different systems. Motivation is to create basic set of services that create the model from a central server (ala SOA) yet still have rich model in the Service Layer. \$\endgroup\$ – James Jan 29 '11 at 3:33
1
\$\begingroup\$

Let's not get pattern tastic.

You have an employee, that's good.

Then you have a way to find them from various sources? Let's call that Employees. Then you are able to perhaps get them and find them. So you have Employees.findByName() - where you expect that there is one, and returns an Employee or throws. Then you might have a queryByName() where you don't know if there is one at all, which might return a list or iterable.

Then you have some different implementations. I personally think that Impl is a terrible thing. It adds more letters without actually telling you more about the implementation. Wr decided the interface was Employees, so now we have maybe an HttpEmployees or a HibernateEmployees, see we implemented the interface, gave more information about the implementation, but didn't need to use Impl.

You've made a bit of an error by putting the EmployeeService (what I called Employees) in the Employee class. The Employee should not know about keeping records on the Employee.

In addition, you should be very wary of static methods here, in this case you would be saying that in a particular application, employees can only be found from one source , because you have a static. Why not instantiate those things that need to use the Employees interface with a particular implementation...

One more thing... getAllEmployees() is unlikely to be useful. Many companies have tens or hundreds of thousands of employees.....

|improve this answer|||||
\$\endgroup\$
  • \$\begingroup\$ I disagree with the notion that static methods for finders is bad and that "Employee should not know about keeping records of Employee." Those two patterns are how modern ORM tools like ActiveRecord(Ruby) and GORM(Groovy) do it. Besides, having Employee now how to insert/update/delete itself is classic EAA Active Record pattern. See an example of GORM for what I mean: grails.org/doc/1.0.x/guide/… \$\endgroup\$ – James Jan 30 '11 at 19:36
  • \$\begingroup\$ The static method introduces tight coupling, which can make unit testing difficult (you won't be able to use a mock Employee, for example). Ruby can get away with it because it is so dynamic (a test can change the Employee class to MockEmployee at runtime) \$\endgroup\$ – Brad Cupit Jan 31 '11 at 13:47
  • \$\begingroup\$ Also, unless you're using a very nice ORM (meaning, if you'll have any code that is using SQL directly, I would put that in a DAO, and have your object model use the DAO. \$\endgroup\$ – Brad Cupit Jan 31 '11 at 13:48
  • \$\begingroup\$ I see two mocking strategies: 1) separate Employee interface from implementation so you can mock Employee and 2) mock EmployeeService inside Employee used in static finders. Niether are hindered from the use of static finder methods. \$\endgroup\$ – James Jan 31 '11 at 17:21
0
\$\begingroup\$

You will have a hard day to "combat" this design. My view on this subject: People either want to get around semantics, do not care about it or even don't know that such thing exists.

As the "Anemic Domain Model" does not care about encapsulation it is easy to get around semantics. You do not need to think about where attributes and algorithms are located "best". You can easily pass the structures around that you need at a certain location to do whatever you want with it.

I have to explain "best" because this is the main point I always struggling with other developers.

I suggest to read the next paragraph too if you get a bad impression on my view here. First of all I want to mention that for me there is for one special requirement only ONE (the "best") solution. The point is: If you talk about code quality and what kind of code is better everybody implicitly say "There is one best solution". If you do not do this: ANY sugestions about "improving" code would be totally subjective.

On the other side WE ALL have cognitive trouble at some point of complexity and/or the programming languge hinders us to express the semantic properly. That is why I am convinced that we nearly NEVER reach the point of the "best" implementation. BUT I am also convinced that we can evaluate the path through trial and error and falsification. Nothing else is done in in science. They try to produce good models of reality. And they want to be as close as possible because they want to make forecasts with these models that will be beneficial for the society. But to do that they have to be clear in semantics.

One thing a want to mention on which assumption my statements are made if I talk about "reality":

I think that we share and experience ONE reality we have to deal with. The other thing is that we all have different perceptions of reality. And our goal should be to determine the best methods to explain reality.

We developers try to produce models od reality as well. But there are developers that do not care about reality as there are people who do not care about the reality that science try to evaluate and describe. The point is: We as developers can make up fiction in the programm in our programming languages as long as the interfaces produce beneficial results to those who hired us. We can unneccessarily iterate over a collection twice but returning the correct result. We can build up huge structure and destroy them instantly. I have to admit that my models are bad in contrast to the models that science produce. Doens't matter if it the fault of the programming language or my cognitive troubles.

So we as developers build models... May goal is it to have the model matching best with reality AND the code we produce matching best the model. Currently object-orientated paradigm is seen as the best paradigm to model real world elements. You have a car in reality, you create a model of a car, finally you get an object of class "Car" in your object-oriented language. A car in reality can be started, so you may have a model of starting engine of your car that may result in a method "startEngine()".

And here comes the problem: The more specific AND the more abstract the things are we have to model: I think we get cognitive troubles. This unfortunately also correlates with the persons perceptions on reality.

We have one great advantage over the management: We can only be tracked by other developers that are familar with the our code. This has been discovered at a point of time when software came up that could not be developed by one developer alone. Collaboration was neccessary. The problem: The different perceptions of reality of the different developers (persons).

Todays approaches to solve this problem are to have experience, IT expertise AND high social competence.

I personally do not care about social competence when I answer questions here. So I put out the sharpest sword have to argue with to pass it to you: The SOLID-principles together with the Law of Demeter.

I am convinced under the assumption I made (reality, semantics etc.) these principles will improve code quality on the semantic level and reveal things that compromise reality so you are able to adjust your model to fit reality better.

The SOLID-principles will lead to a domain model that clearly is NOT an "Anemic Domain Model".

|improve this answer|||||
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.