4

I am coding a ribbon/achievements system for a website and I have to write some logic for each ribbon in my system. For example, you could earn a ribbon if you're in the first 2,000 people registering to the website or after 1,000 post in the forum. The idea is very similar to stackoverflow's badges, really.

So, every ribbon is obviously in the database but they also need a bit of logic to determine when a user has earned the ribbon.

In the way I coded it, Ribbon is a simple interface:

public interface Ribbon {
    public void setId(int id);
    public int getId();
    public String getTitle();
    public void setTitle(String title);
    public boolean isEarned(User user);
}

RibbonJpa is an abstract class that implements the Ribbon interface, avoiding the definition of the isEarned() method:

@Entity
@Table(name = "ribbon")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "ribbon_type")
public abstract class RibbonJpa implements Ribbon {
    @Id
    @Column(name = "id", nullable = false)
    int id;

    @Column(name = "title", nullable = false)
    private String title;

    @Override
    public int getId() {
        return id;
    }

    @Override
    public void setId(int id) {
        this.id= id;
    }

    @Override
    public String getTitle() {
        return title;
    }

    @Override
    public void setTitle(String title) {
        this.title = title;
    }
}

You can see I define the inheritance strategy as SINGLE_TABLE (since I have to code like 50 ribbons and I don't need additional columns for any of them).

Now, a specific ribbon will be implemented like this:

@Entity
public class FirstUsersRibbon extends RibbonJpa implements Ribbon {
    public FirstUsersRibbon() {
        super.setId(1);
        super.setTitle("First 2,000 users registered to the website");
    }

    @Override
    public boolean isEarned(User user) {
        // My logic to check whether the specified user has won the award
    }
}

This code works fine, the tables are created in the database in the way I expect (I use DDL generation in my local environment).

The thing is, it feels wrong to code business logic in a domain object. Is it good practice? Can you suggest a better solution? Also, I'm not able to Autowire any DAOs in the entity (FirstUsersRibbon) and I need them in the business logic (in this case, I need a DAO to check whether the user is in the first 2,000 users registered to the website).

Any help very appreciated.

Thank you!

satoshi
  • 3,963
  • 6
  • 46
  • 57
  • Have you heard about SOLID? I think you are breaking 2 principles here. The LSP (in your class RibbonJpa implementing Ribbon) and the ISP(the methods signature in Ribbon) – karlihnos Aug 17 '17 at 15:23

2 Answers2

12

The thing is, it feels wrong to code business logic in a domain object.

Many would say the reverse was true: that it is an anti-pattern (an anaemic domain model) to have business logic anywhere else. See Domain-Driven Design for more information.

You might then wonder what the middle tier of the conventional 3-tier architecture was for. It provides a service layer for the application. See my related question "What use are EJBs?".

Community
  • 1
  • 1
Raedwald
  • 46,613
  • 43
  • 151
  • 237
  • Thank you for this information, @Raedwald. Good to know I'm following the right way :) Do you know why I'm not able to Autowire DAOs (or any other Component) in the domain object? Any solutions? Thanks. – satoshi May 20 '12 at 20:18
  • I'm new to Spring, so I can't help with that. It sounds like you ought to ask a separate question. – Raedwald May 20 '12 at 20:45
  • Note 1: If you plan to implement business logic in entities, be aware that if you pass the same as detached objects, clients have access to business logic too. Clients must be supplied with data only and not business logic. Note 2: `FirstUsersRibbon` need not explicitly implement `Ribbon` interface as `RibbonJpa` does it. – lupchiazoem Jun 01 '12 at 10:54
0

Also, I'm not able to Autowire any DAOs in the entity

If you're using Spring and Hibernate, have a look at http://jblewitt.com/blog/?p=129: this gives a good description of a similar problem with a variety of solutions.

If you're looking for a rich domain model in the way that you describe, then it can be a good idea to instantiate domain objects via Spring, and hence be able to inject DAOs into your domain objects.

Kkkev
  • 4,716
  • 5
  • 27
  • 43
  • It's not the SO style is to put supplementary question as answers. You ought to ask this as a separate question. – Raedwald May 20 '12 at 20:48