Where is Martin Fowler when I need him? :)
Brian Pontarelli wrote:
> In terms of the logic for the calculateIncomeTax method, it sounds
> like that is going to be on the server. This means that the client
> version of the Person class will primarily be a proxy to the server.
> Therefore, it doesn't necessarily need to be "rich" because the logic
> is not inside the class itself, but instead defined somewhere else.
> All that you are gaining using a "rich" approach is that you get to
> interact with just the Person class. Once you get to this point, there
> is very little difference between this methodology and using anemic
> domain and services.
For this case there is indeed little difference between the anemic
model and rich model. But for this specific use-case I would flip the
question on you and ask: what do you benefit from a anemic model? Given
the point of using C-style functions and global variables versus OOP why
wouldn't you go for OOP every time?
> I guess that is my main point. The "rich" domain folks talk about
> putting logic and data in the same place. But when the logic gets
> complex or changes frequently, you _have_ to pull it out into a
> service that conforms to an interface.
You have to pull it into an interface, yes, but not necessarily into a
service. Nothing prevents you from defining an interface for
calculateIncomeTax() and providing different implementations in the form
of rich models.
> Then it makes sense to put that
> logic on another server so that it can be re-used and can scale. Then
> you start looking at ways of swapping in new implementations of that
> interface at runtime to make updates easier.
That's a requirement I never had in practice. My assumption has been
that if you have a fail-safe cluster in the first place then you just
update different servers at a time. I find the idea of swapping
implementations at runtime as a form of upgrading quite questionable. It
sounds like it would be much harder to update all necessary classes
atomically and track/log problems if something goes wrong.
> And you think about
> service discovery and all that jazz and BAM, your client is anemic.
> You've pulled the logic as far away from the data as you could. Now,
> you can still have a "rich" domain on the server. However, if you run
> into similar cases on the server, you'll just do it again.
If you absolutely need service discovery then I agree that pretty much
mandates anemic models, but again I question how frequently this
requirement comes up.
Gili
> I also think this only applies in certain places. Those are business
> domains with business logic. If it looks like to could ever be
> distributed, I say make it anemic and service based.
>
> -bp
>
>
>
>
> On Nov 7, 2008, at 4:51 PM, Gili Tzabari wrote:
>
>>
>> I was actually going to bring up #2 myself in the last post but I
>> forgot. I believe that in most cases Person.marry(Person other) will
>> work but sometimes the implementation requires you to access
>> information
>> that spans beyond the state of a single person. Because you never know
>> how the implementation might evolve over time I always prefer placing
>> such methods at the Persons level. That is, any business logic
>> involving
>> a single person goes into Person while logic involving multiple
>> parties
>> goes into People. That's just a rule of thumb (feel free to violate it
>> if it makes sense).
>>
>> With respect to #1 I would suggest injecting two different
>> implementations of Persons for the client and server. That is, the
>> code
>> lives inside the Persons instance, but the concrete implementation
>> would
>> vary.
>>
>> Again, I am sort of making this up as I go along but it feels right
>> so far.
>>
>> Gili
>>
>> Brian Pontarelli wrote:
>>> A few questions:
>>>
>>> 1. Where is the implementation (actual code) for the /Money
>>> calculateIncomeTax(); method located?/
>>>
>>> 2. Why isn't the /marry()/ method on Person like this: /marry(Person
>>> other);/?
>>>
>>> -bp
>>>
>>>
>>>
>>> On Nov 7, 2008, at 3:34 PM, Gili Tzabari wrote:
>>>
>>>>
>>>> I am actually toying around with a 3rd paradigm. I'm not sure
>>>> whether
>>>> it falls under the category of anemic models, rich domain model or
>>>> something else. Judge for yourself:
>>>>
>>>> // Cat + business logic
>>>> class Person
>>>> {
>>>> private String name;
>>>>
>>>> String getName();
>>>> Money calculateIncomeTax();
>>>> }
>>>>
>>>> \-> Key point: all methods operate on the current Person. Unlike
>>>> anemic
>>>> models, you store business logic methods at this level so long as
>>>> they
>>>> apply to a single Person (i.e. calculateIncomeTax()).
>>>>
>>>> // CatService + state
>>>> class People
>>>> {
>>>> private List<Person> people;
>>>>
>>>> void marry(Person first, Person second);
>>>> List<Person> getMarriedPeople();
>>>> }
>>>>
>>>> You would reuse the same Person implementation on both client and
>>>> server while varying the implementation of People. For your cats
>>>> example, you'd have Cat and Cats instead of Cat and CatService.
>>>>
>>>> Gili
>>>>
>>>>
>>>> Brian Pontarelli wrote:
>>>>>> Are you talking about wanting to be able to represent a Cat with
>>>>>> fix()
>>>>>> on the server and without fix() on the client because of business-
>>>>>> logic?
>>>>>> Or are you saying that technical limitations might cause fix()
>>>>>> to fail
>>>>>> on the client? If it's the latter I argue that it's no different
>>>>>> from
>>>>>> the server because even there the DB might become unreachable. If
>>>>>> you're
>>>>>> arguing the former, then I argue you need different client-side
>>>>>> and
>>>>>> server-side Cat interfaces because they are not really the same.
>>>>> What I'm saying is that the fix method must be different on the
>>>>> client
>>>>> and server. This generally means a different class for the client
>>>>> and
>>>>> server. If it was the same class on the client and server, all the
>>>>> logic would need to be pull out into a well known interface and
>>>>> that
>>>>> interface would need to be different implementations on the
>>>>> client and
>>>>> server, making the domain just a proxy and not very rich.
>>>>>
>>>>>
>>>>>> I guess I am saying that you shouldn't be passing Rich Domain
>>>>>> Objects
>>>>>> over the wire because (from practical experience) the server-
>>>>>> side and
>>>>>> client-side interfaces are almost always "different enough" that
>>>>>> you
>>>>>> can't share the interfaces. Clients usually contain a subset of
>>>>>> the
>>>>>> server methods and might contain some client-only methods. I
>>>>>> believe
>>>>>> Rich Domain Objects still hold a lot of value on the server-end,
>>>>>> even if
>>>>>> you can't pass them to clients. Clients and servers can both run
>>>>>> their
>>>>>> own show (with RDO) and communicate over the wire by passing a
>>>>>> subset of
>>>>>> the state (DTO, REST, whatever you want to call it).
>>>>> Yeah, we agree on this. You shouldn't be passing around rich domain
>>>>> objects because they don't work that way. This incurs a lot of code
>>>>> overhead though.
>>>>>
>>>>>
>>>>>> I argue you always need to define the client-server interface by
>>>>>> hand.
>>>>>> Serializing your state-only Object to the client is very poor
>>>>>> practice.
>>>>>> One reason is that you're essentially exposing an interface to the
>>>>>> client and this needs to be designed and exposed very carefully.
>>>>>> The
>>>>>> other reason is that you can't rely on having Java on both ends of
>>>>>> the wire.
>>>>> Not sure what you mean by this. You definitely have to define how
>>>>> your
>>>>> client talks to the server and what your server responds with,
>>>>> since
>>>>> that is the standard remoting paradigm. In terms of serializing a
>>>>> state only object as poor practice, I think you might be confused,
>>>>> because that's what you said you should do above with the DTO/REST/
>>>>> whatever. DTOs are state objects.
>>>>>
>>>>> Again, here's a concrete example so we are on the same page:
>>>>>
>>>>> RDO
>>>>> -------
>>>>>
>>>>> public class ClientCat {
>>>>> @Inject RemoteCatService cs;
>>>>> boolean fixed;
>>>>>
>>>>> public void fix() {
>>>>> CatDTO dto = new CatDTO();
>>>>> dto.fixed = this.fixed;
>>>>> this.fixed = cs.fixTheCat(dto);
>>>>> }
>>>>> }
>>>>>
>>>>> public class ServerCat {
>>>>> @Inject EntityManager em;
>>>>> boolean fixed;
>>>>>
>>>>> public void fix() {
>>>>> this.fixed = true;
>>>>> em.persist(this);
>>>>> }
>>>>> }
>>>>>
>>>>> public class CatDTO {
>>>>> boolean fixed;
>>>>> }
>>>>>
>>>>> public class Client {
>>>>> public void fixTheCat() {
>>>>> ClientCat cat = DI.makeCat();
>>>>> cat.fix();
>>>>> }
>>>>> }
>>>>>
>>>>> // This class runs on a different server
>>>>> public class RemoteCatService {
>>>>> public void fixTheCat(CatDTO dto) {
>>>>> ServerCat cat = DI.makeCat();
>>>>> cat.fixed = dto.fixed;
>>>>> cat.fix();
>>>>> }
>>>>> }
>>>>>
>>>>>
>>>>> Anemic
>>>>> ----------
>>>>>
>>>>> public class Cat {
>>>>> boolean fixed;
>>>>> }
>>>>>
>>>>> public class Client {
>>>>> @Inject RemoteCatService cs;
>>>>>
>>>>> public void fixTheCat() {
>>>>> Cat cat = new Cat();
>>>>> cat = cs.fixTheCat(cat);
>>>>> }
>>>>> }
>>>>>
>>>>> // This class runs on a different server
>>>>> public class RemoteCatService {
>>>>> @Inject EntityManager em;
>>>>>
>>>>> public Cat fixTheCat(Cat cat) {
>>>>> cat.fixed = true;
>>>>> em.persist(cat);
>>>>> return cat;
>>>>> }
>>>>> }
>>>>>
>>>>>
>>>>> I like the second one personally. Less code and easy to manage.
>>>>> Plus,
>>>>> I can pass my Cat around anywhere I want without transforming it
>>>>> into
>>>>> other objects. In n-tiered systems, you could potentially pass
>>>>> the Cat
>>>>> all the way from the front-end to the last tier and then back up.
>>>>> This
>>>>> is how we built most stuff at Orbitz.
>>>>>
>>>>> -bp
>>>>>
>>>>
>>>>
>>>
>
>
> >
>
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"google-guice" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at
http://groups.google.com/group/google-guice?hl=en
-~----------~----~----~----~------~----~------~--~---