I'm trying to figure out the options that I have for the architecture of my API project.
I would like to create an API using JAX-RS version 1.0. This API consumes Remote EJBs (EJB 3.0) from a bigger, old and complex application. I'm using Java 6.
So far, I can do this and works. But I'm not satisfied with the solution. See my packages disposition. My concerns are described after the code:
/api/
/com.organization.api.v1.rs -> Rest Services with the JAX-RS annotations
/com.organization.api.v1.services -> Service classes used by Rest Services. Basically, they only have the logic to transform the DTOs objects from Remote EJBs in JSON. This is separated by API version, because the JSON can be different in each version.
/com.organization.api.v1.vo -> View Objects returned by the Rest Services. They will be transformed in JSON using Gson.
/com.organization.api.services -> Service classes used by versioned Services.
Here we have the lookup for Remote EJBs and some API logic, like validations. This services can be used by any versioned of each Service.
Example of the com.organization.api.v1.rs.UserV1RS:
@Path("/v1/user/")
public class UserV1RS {
@GET
public UserV1VO getUsername() {
UserV1VO userVO = ServiceLocator.get(UserV1Service.class).getUsername();
return userVO;
}
}
Example of the com.organization.api.v1.services.UserV1Service:
public class UserV1Service extends UserService {
public UserV1VO getUsername() {
UserDTO userDTO = getUserName(); // method from UserService
return new UserV1VO(userDTO.getName);
}
}
Example of the com.organization.api.services.UserService:
public class UserService {
public UserDTO getUsername() {
UserDTO userDTO = RemoteEJBLocator.lookup(UserRemote.JNDI_REMOTE_NAME).getUser();
return userDTO;
}
}
Some requirements of my project:
- The API have versions: v1, v2, etc.
- The different API versions of the same versioned Service can share code:
UserV1ServiceandUserV2ServiceusingUserService. - The different API versions of different versioned Services can share code:
UserV1ServiceandOrderV2ServiceusingAnotherService. - Each version have his own View Object (
UserV1VOand notUserVO).
What botters me about the code above:
- This
ServiceLocatorclass it not a good approach for me. This class use legacy code from an old library and I have a lot of questions about how this class works. The way to use theServiceLocatoris very strange for me too and this strategy is not good to mock the services for my unit tests. I would like to create a new ServiceLocator or use some dependency injection strategy (or another better approach). - The
UserServiceclass is not intended to be used by another "external" service, likeOrderService. It's only for theUserVxService. But in the future, maybeOrderServicewould like to use some code fromUserService... - Even if I ignore the last problem, using the
ServiceLocatorI will need to do a lot oflookupsamong my code. The chance of create a cyclic dependency (serviceOne lookup serviceTwo that lookup serviceThree that lookup serviceOne) is very high. - In this approach, the VOs, like
UserV1VO, could be used in my unversioned services (com.organization.api.services), but this cannot happen. A good architecture don't allow something that is not allowed. I have the idea to create a new project, likeapi-servicesand put thecom.organization.api.servicesthere to avoid this. Is this a good solution?
So... ideas?