Sterling Duchess Sterling Duchess - 9 months ago 20
Java Question

Why do I need a repository and a service + contract

I'm new to Spring coming from PHP/Larvel, Ruby/Rails and Python/Django. Coming from these frameworks I'm used to only seeing the Model (Entity/Dao?) and everything else is handled by the Framework/ORM/QueryBuilders thus all there is for me to do is declare a model and add to it any operations/relations/methods I need.

In these frameworks this is all I would have:

class User extends BaseModel
{
public function devices() { .. } // Relation to Device model

// Any additional functions on user go here these can operate on
// just the User model or pull additional data from DB or other models.
public function assignUserToDevice();
public function getUsersScore();
public function connectUserToService();
}


However following this Spring tutorial I now have this:

Model (Entity/Dao ?): Only contains properties no methods except for GET/SET and relations.

@Entity
@Table(name = "users")
class User {
@Id
@GeneratedValue
var id: Long = 0

var username: String = "" //...
}


Repository:

interface UserRepository : CrudRepository<User, Long> {
fun findByUsername(username: String): User
}


UserServiceContract:

interface UserServiceContract {

/**
* Attempts to find User associated with the given id.
* @throws ModelNotFoundException if not user has been found associated with the given id.
* @param id given users id.
* @return User associated with the given id.
*/
@Throws(ModelNotFoundException::class)
fun find(id: Long): User

/**
* Returns all existing users.
* @return list of all existing users.
*/
fun findAll(): List<User>
}


And a service:

@Service
class UserService : UserServiceContract {

@Autowired
lateinit var userRepository: UserRepository

/**
* Attempts to find User associated with the given id.
* @throws ModelNotFoundException if not user has been found associated with the given id.
* @param id given users id.
* @return User associated with the given id.
*/
override fun find(id: Long) = userRepository.findOne(id) ?: throw ModelNotFoundException("User not found!")

override fun findAll(): List<User> {
throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}


Questions:


  1. Why do I need a
    UserRepository
    and
    UserService + UserServiceContract
    ? From what I read on Spring guides on Repositories you already get the default
    CRUD
    methods
    save, read, update, delete..
    on top of that you also have query methods where you can do
    findUserBy...
    or write annotated raw queries. So I'm a bit confused why than declare a
    UserServiceContract
    and
    UserService
    just to implement that functionality which is already provided by the repository ?

  2. Previously I would keep methods for working on/with my Model in said Model however as I noticed from few resources this is not where they go in Spring so where do I keep them ? Is this why there is a
    UserService
    and
    UserServiceContract
    ?
    Should I really use the
    UserRepository
    for database access only and in
    UserServiceContract
    declare methods for working on/with the User like:
    public function connectUserToService()
    ?


Answer Source

The User is the entity. It basically represents a row from a table in the database. And it can definitely have methods. It's usually not a good idea to put methods that don't relate to that entity only in the entity class. Especially not methods that would need access to external dependencies (like repositories or services).

The Repository is the component allowing to execute queries related to a given entity, or to save new instances of that entity.

The service is what contains the business logic. It can be as simple as simply delegating to a repository, but unless your app just consists in viewing information from the database, it often contains more complex logic, involving several entities and repositories.

Separating the repository from the service is useful because it allows

  • avoiding mixed responsibilities,
  • testing the queries easily, without having to invoke complex logic using them
  • testing the business logic easily and efficiently by mocking the repositories
  • demarcating transaction boundaries: a service method is typically a transaction.

Whether you use an interface or not to define the contract of your service is up to you. Spring doesn't force you to do that. You can also mix the service and the repository if you want to shoot yourself in the foot, but Spring doesn't encourage it, and you won't be able to do that if you use spring-data-jpa, which basically generates a repository implementation for you based on an interface.

I don't know much about the frameworks you're used to, but you need to keep this in mind when thinking about the design of a spring app: Spring is a dependency injection framework, which thus consists in injecting components into other components. Entities are not Spring components, and thus can't be injected with the dependencies they would need if they contained business logic (which again, would mix responsibilities, IMO). And dependency injection itself is mainly useful to make the code testable, and to be able to add aspects around method invocations (to start and commit transactions, for example). That's what drives the Spring design: single-responsibility principle, testability thanks to dependency injection, and .