Announcement Announcement Module
Collapse
No announcement yet.
Rich domain model, @Configurable and where to place "select" methods Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Rich domain model, @Configurable and where to place "select" methods

    Hi all.

    In my project I'm trying to use rich domain model. For example, I have a User class which is annotated with @Configurable so I can inject UserDao into it:

    Code:
    /**
     * User Entity.
     */
    @Entity
    @Table(name = "users")
    @Configurable
    public class User implements Serializable {
        /**
         * Default no-arg constructor.
         */
        public User() {
        }
    
        public User(String username, String password, boolean enabled) {
            this.password = password;
            this.enabled = enabled;
            this.username = username;
        }
    
        @Id
        @GeneratedValue
        private Integer id;
    
        public Integer getId() {
            return id;
        }
    
        @Column(nullable = false, unique = true)
        private String username;
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        @Column(nullable = false)
        private String password;
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        @Column(nullable = false)
        private boolean enabled;
    
        public boolean isEnabled() {
            return enabled;
        }
    
        public void setEnabled(boolean enabled) {
            this.enabled = enabled;
        }
    
        /**
         * Autowired UserDao instance.
         */
        @Autowired
        @Transient
        transient private UserDao userDao;
    
        public void save() {
            userDao.save(this);
        }
    
        public void delete() {
            userDao.delete(this);
        }
    }
    This gives the possibility to invoke methods like

    Code:
    user.save();
    user.delete();
    in pure object-oriented style. I really like this approach but I do not understand where should I better place methods like getByUsername(...) etc. My Services layer invokes methods on entities and doesn't now about any DAOs. So how is better to handle this? Inject DAOs into Services or invent something like UserFactory.getByUsername(...)?

    Any suggestions?

    Regards,
    Alexander

  • #2
    First of all, what you have described is not a RDD, but rather "Active Record" pattern. Rich Domain Model (at least how I understand it) is about implementing of a business logic, not a persistence logic inside domain objects.

    Next, in the Active Record pattern finder methods (like your getByUsername(...) ) are typically coded as class methods (in Java case static methods), but such style does not go well along with Spring (and DI in general).

    So, I dare suggest you to re-think your design carefully.


    Originally posted by Bohtvaroh View Post
    Hi all.

    In my project I'm trying to use rich domain model. For example, I have a User class which is annotated with @Configurable so I can inject UserDao into it:

    Code:
    /**
     * User Entity.
     */
    @Entity
    @Table(name = "users")
    @Configurable
    public class User implements Serializable {
        /**
         * Default no-arg constructor.
         */
        public User() {
        }
    
        public User(String username, String password, boolean enabled) {
            this.password = password;
            this.enabled = enabled;
            this.username = username;
        }
    
        @Id
        @GeneratedValue
        private Integer id;
    
        public Integer getId() {
            return id;
        }
    
        @Column(nullable = false, unique = true)
        private String username;
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        @Column(nullable = false)
        private String password;
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        @Column(nullable = false)
        private boolean enabled;
    
        public boolean isEnabled() {
            return enabled;
        }
    
        public void setEnabled(boolean enabled) {
            this.enabled = enabled;
        }
    
        /**
         * Autowired UserDao instance.
         */
        @Autowired
        @Transient
        transient private UserDao userDao;
    
        public void save() {
            userDao.save(this);
        }
    
        public void delete() {
            userDao.delete(this);
        }
    }
    This gives the possibility to invoke methods like

    Code:
    user.save();
    user.delete();
    in pure object-oriented style. I really like this approach but I do not understand where should I better place methods like getByUsername(...) etc. My Services layer invokes methods on entities and doesn't now about any DAOs. So how is better to handle this? Inject DAOs into Services or invent something like UserFactory.getByUsername(...)?

    Any suggestions?

    Regards,
    Alexander

    Comment


    • #3
      Thank you for answering.

      I want to clear my self. What I meant by "rich" - is the domain model which is not anemic. It knows how to save, delete itself and possesses it's behaviour as methods. It's like DDD I think, I didn't know about RDD earlier. I don't think it is implementing persistence logic - it delegates such a task to the dao. Perhaps, this is a not so good example but imagine that there are other methods implementing a business logic in User class.

      For now I've introduced the UserFactory class which possesses methods for creating the User entity:

      Code:
      @Component
      public class UserFactory {
          /**
           * Autowired UserDao instance.
           */
          @Autowired
          private UserDao userDao;
      
          /**
           * Retrieves User entity by name.
           *
           * @param username user name
           * @return user entity
           */
          public User getByUsername(String username) {
              return userDao.getByUsername(username);
          }
      }
      which is actually a helper class for the model.

      What do you think?

      Originally posted by al0 View Post
      First of all, what you have described is not a RDD, but rather "Active Record" pattern. Rich Domain Model (at least how I understand it) is about implementing of a business logic, not a persistence logic inside domain objects.

      Next, in the Active Record pattern finder methods (like your getByUsername(...) ) are typically coded as class methods (in Java case static methods), but such style does not go well along with Spring (and DI in general).

      So, I dare suggest you to re-think your design carefully.

      Comment


      • #4
        Originally posted by Bohtvaroh View Post
        Thank you for answering.

        I want to clear my self. What I meant by "rich" - is the domain model which is not anemic.

        It knows how to save, delete itself and possesses it's behaviour as methods. It's like DDD I think, I didn't know about RDD earlier.
        I understood you exactly this way - and you have used "Rich Domain Model" in the title of your original post and RDD is merely abbreviation of it, not some kind of standard term.

        And possessing a knowledge about the persistence does not make model "non-anemic". The persistence is rather cross-cutting concern in respect to domain-specific behavior and for this reason better be kept out of domain objects (and I mean not implementation details but the concept as such).

        I don't think it is implementing persistence logic - it delegates such a task to the dao.
        So delegation does not help much in this case.
        Perhaps, this is a not so good example but imagine that there are other methods implementing a business logic in User class.
        Perhaps, and even likely - but it is unlikely that such methods are not tied to the specific existing instance of the domain object - in contrast to finder methods.
        For now I've introduced the UserFactory class which possesses methods for creating the User entity:
        ...
        What do you think?
        I think that user factory is just a disguised service - but somewhat crippled as a result code responsible for the persistence operations becomes dissipated over the several classes.

        BTW, whatever Fowler writes (and I rather regret that his name is not Fouler ), it is very arguable that Anemic Domain Model is necessarily anti-pattern.
        Many (if not a majority) of the business applications really have a tiny domain-specific behavior.

        Comment


        • #5
          I agree with al0, I think that Fowler and Evans' labeling (anemic vs. DDD, anti-pattern, etc.) is unfortunate. I find that most such labeling exercises eventually lead to dogmas. And the thing about any dogma is that people often start following it without much critical thinking. I have myself gone through the "object bigotry" period in my software career, but quickly discovered that proper separation of concerns and modularization still remain the most important principle of software engineering and should be the priority in design/modeling considerations. That said, the question is not whether behavior belongs on objects (of course, it does!) but which objects should encapsulate which behavior. And - after many years and countless real-life projects - I am convinced that it is best to keep actors separate from the scenarios they may (or may not!) participate in. I know many people do not believe in software/object/module reuse, but I do - wholeheartedly. It is a life saviour for me. I enjoy designing software where each peace makes sense, is manageable, and can be potentially reused in ever-changing business requirements. Burning business scenarios into domain entities is a killer to me. I consider a service (perhaps, "service" is a misnomer that makes things sound less OO) a valid part of the domain model, it is simply a separate object that implements scenarios that are better off not coupled with the actors. Although I certainly respect Fowler for being a very valuable and influential voice in the software community (e.g. I very much appreciate his evangelizing of re-factoring), I tend to disagree with his "bigoted" view on this particular subject. In practice, it too often results in people producing models that have very little in common with real-life concepts they intend to represent; such models are therefore difficult to understand and maintain; not to mention, they completely lack flexibility. I do not dismiss the concept completely, but everything is good in moderation, and - as with everything - great caution and much thought should be applied when deciding which functionality belongs on each particular object. The more your model mimics the real life relationships, the more natural it is, the more flexible and easy to understand it is, and the more robust and healthy your system is, ultimately.

          Persistence should always be externalized, in my opinion, and it should be abstracted from clients behind the logical scenarios (use cases) that may or may not require persistence. For example, placing an order may require the order data to be persisted, however it is of no interest to the clients that place orders. The customers just submit the request and expect the product in return. They don't care where, how, and when any data is saved; they want the product. So, any persistence logic should be just a hidden implementation detail abstracted within the object that implements the mechanism to place the order, while the client is only exposed to a very abstract "place order" API.
          Last edited by constv; Apr 18th, 2009, 11:00 AM.

          Comment

          Working...
          X