Daniel
Daniel

Reputation: 2500

Play 2.4: How to separate concerns with Ebean?

So recently started on a new web app which will be using Play 2.4. I've been using JPA for a lot of projects, but I decided to give Ebean a shot this time.

However I'm wondering about the separation of concerns. With JPA I would typically create models, services and DAOs for persistence. With Ebean I understand that there's support for static methods on the models (see http://ebean-orm.github.io/quickstart "Create Model").

I've seen quite a few examples where everything seems to go into these static methods sitting in the models; business logic, persistence logic and what not. Is this best practice when using Ebean that I should just embrace? Or does it still make sense to separate the logic using services and DAOs, and who would I go about doing that in the best way?

Upvotes: 1

Views: 919

Answers (1)

Steve Chaloner
Steve Chaloner

Reputation: 8202

You can do things exactly as you did with JPA, and create DAOs, etc.

If you check the static methods in the Ebean examples, you'll see they typically use a Finder. For a table Country whose primary key is a String iso2, you would have a model class like this:

@Entity
public class Country extends Model {
    @Id
    public String iso2;

    // other stuff
}

In an Ebean example ActiveRecord-style of code, there would be some additional stuff.

@Entity
public class Country extends Model {

    private static final Finder<String, Country> FIND = new Finder<>(String.class, Country.class);

    @Id
    public String iso2;

    public static List<Country> findAll() {
        return FIND.all();
    }

    // other stuff
}

If you wanted to change this to a DAO-style approach, you just need to move the Finder-related code out of Country.

public class CountryDao {
    private static final Model.Finder<String, Country> FIND = new Model.Finder<>(String.class, Country.class);

    // not static if you don't want it to be
    public List<Country> findAll() {
        return FIND.all();
    }
}

Which approach you use is getting into opinion territory, but the DAO approach has a lot to recommend it, especially now Play supports DI out of the box. Instead of having a controller that makes hard-coded references to model classes, you can just inject your DAOs.

// direct reference to the Country class
public class ControllerWithActiveRecord extends Controller {
    public F.Promise<Result> getAllCountries() {
        return F.Promise.promise(() -> Country.findAll())
                        .map(Json::toJson)
                        .map(Results::ok);
    }
}

Alternate using DI.

public class ControllerWithInjectedDAO extends Controller {

    private final CountryDAO countryDAO;

    @Inject
    public ControllerWithInjectedDAO(final CountryDAO countryDAO) {
        this.countryDAO = countryDAO;
    }

    public F.Promise<Result> getAllCountries() {
        return F.Promise.promise(() -> countryDAO.findAll())
                        .map(Json::toJson)
                        .map(Results::ok);
    }
}

Upvotes: 3

Related Questions