Hacker News new | past | comments | ask | show | jobs | submit login

    > I am curious where this comes from, because my thinking is the absolutely opposite. As much business logic as possible should belong in the model.
The opposite of this is what Fowler has called an "Anemic Domain Model"[0] which is ostensibly an anti-pattern. What I've learned from my own experience is that with an anemic domain model, the biggest challenge is that the logic for mutating that object is all over the codebase. So instead of `thing.DoDiscreteThang()`, there could be one or more `service1.DoDiscreteThang(thing)` and `serviceN.DoDiscreteThang(thing)` because the author of `service1` didn't know that `service2` also did the mutation.

Domain models are hard to do well and I think the SOA era brought a lot of confusion between data transfer objects, serialized objects, anemic domain models, and domain models.

[0] https://martinfowler.com/bliki/AnemicDomainModel.html




>the biggest challenge is that the logic for mutating that object is all over the codebase

Just use immutable data structures and be done with it. In departing from old OOP views and becoming more functional programming and data oriented programming friendly, C# introduced Records, which are immutable. Probably Java and Python have similar constructs. Javascript allowed the use of immutable data since long time ago.

If you insist of using fat models, you will still mutate the data all over the place by doing calls, but you just obfuscate it.


> Probably Java and Python have similar constructs

In Python, the closest you can get is a "frozen" dataclass, but you don't get true immutability[0]. What you _do_ get is effective enough for just about all practical use cases, except for the standard foot guns around mutable data structures.

    @dataclass(frozen=True)
    class MyModel:
        ...
[0]: https://docs.python.org/3/library/dataclasses.html#frozen-in...


You can redefine the byte representation `True` corresponds to in python. "Immutable enough" is all you're really looking for; it somebody goes out of their way to mutate the thing then they probably had a good reason for it.


Ahh, Fowler. The author that gave the World such gifts as Dependency Injection, Inversion of Control and other over-engineered "patterns". This is just my opinion obviously, based on experience spanning from the early 90s.


That's like blaming Fleming for the antibiotic crisis. Just because you have a pattern, you shouldn't use it preemptively.


Agreed, although the Java culture took the patterns and applied them in a cargo-cult frenzy. I do think the likes of Fowler and the so called Gang of Four are to blame for many of the Sun's later mistakes in API design and for the culture of patterns-everywhere in that era.


Imho, mutating the same object so many times, that a developer can't easily infer already applied changes is also a strong code smell. Fat models tend to encourage it, since all the mutation logic is available to all the services.


There are ways of getting around this. For instance, the "mutating" code can be organized in the service layer in a single location.

For instance, if you are updating a ShoppingCart model, all of that code which creates/updates/deletes a ShoppingCart could be kept in the ShoppingService - which will also create/update/delete the ShoppingCartItem models which are the line items for each item in the carts. So you don't have one Service class per table - but rather one service class per module of functionality.


The pattern is not OOP but that hardly makes it an anti-pattern.

Personally my take is business logic should be in the services and object specific validation in the like can be in the model. Unless your business logic is meant to deal entirely with single object types at a time you can hardly fit it in the pure OOP dogma. A behavior that deals with ModelA and ModelB seems just at home on serviceAB as it does on either model, from an OOP sense.


I tend to draw the line at intrinsic vs extrinsic behavior. The model layer must be able to maintain all intrinsic properties. Whenever it would talk outside the application, it's beyond the domain of the model.

Taken to the extreme, you could model all intrinsic constraints and triggers at the relational database level, and have a perfectly functional anemic domain model.


In our model we have "repositories" (they dont talk outside the application, they basically contain queries related to a specific db table), and "services" (they call models, do queries that we not related to a specific db table and may talk to outside the application).




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: