用于组织项目的选项:JAX-RS API,ServiceLocator和远程EJB

Dhe*_*rik 8 java architecture jax-rs ejb-3.0 api-versioning

我正在试图弄清楚我对API项目架构的选择.

我想使用JAX-RS 1.0版创建一个API.此API从更大,更旧,更复杂的应用程序中使用远程EJB(EJB 3.0).我正在使用Java 6.

到目前为止,我可以做到这一点并且有效.但我对解决方案不满意.看我的包裹配置.我的问题在代码后面描述:

/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.
Run Code Online (Sandbox Code Playgroud)

示例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;
  }

}
Run Code Online (Sandbox Code Playgroud)

示例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);
  }

}
Run Code Online (Sandbox Code Playgroud)

示例com.organization.api.services.UserService:

public class UserService {

  public UserDTO getUsername() {
    UserDTO userDTO = RemoteEJBLocator.lookup(UserRemote.JNDI_REMOTE_NAME).getUser();
    return userDTO;
  }

}
Run Code Online (Sandbox Code Playgroud)

我项目的一些要求:

  • API有版本:v1,v2等.
  • 相同版本化服务的不同API版本可以共享代码:UserV1ServiceUserV2Service使用UserService.
  • 不同版本的服务的不同API版本可以共享代码:UserV1ServiceOrderV2Service使用AnotherService.
  • 每个版本都有自己的View对象(UserV1VO而不是UserVO).

关于上面的代码让我烦恼的是:

  1. ServiceLocator堂课对我来说不是一个好方法.这个类使用旧库中的遗留代码,我对这个类的工作方式有很多疑问.使用它的方式ServiceLocator对我来说也很奇怪,这个策略不适合模拟 单元测试的服务.我想创建一个新的ServiceLocator或使用一些依赖注入策略(或另一种更好的方法).
  2. UserService类并不旨在由另一个"外部"服务中使用,像OrderService.这只是为了UserVxService.但在将来,也许OrderService想使用一些来自UserService...的代码
  3. 即使我忽略了最后一个问题,使用ServiceLocator我将需要lookups在我的代码中做很多事情.创建循环依赖的机会(serviceOne查找serviceTwo,查找serviceThree,查找serviceOne)非常高.
  4. 在这种方法中,VO UserV1VO可以用在我的无版本服务(com.organization.api.services)中,但这不可能发生.一个好的架构不允许不允许的东西.我有创建一个新项目的想法,api-services并将其放在com.organization.api.services那里以避免这种情况.这是一个好的解决方案吗?

所以......想法?

Shi*_*z.M 2

我看到的一些事情:

  • 理想情况下应该UserService基于接口。它们似乎有类似的契约,但唯一的区别是它们的来源(RemoteEJB、LocalServiceLocator)。这些应该是返回的 DTO

  • UserV1Service extends UserService不应该使用继承,而应该倾向于组合。考虑一下您需要为同一服务的 v2 做什么。根据你的例子,你会得到UserV2Service extends UserService. 这并不理想,特别是如果您最终在基类中使用特定于某个版本的抽象方法。然后突然间其他版本化服务需要满足这一需求。

对于服务定位器

  • 在您的情况下,您最好使用 Spring 或 CDI 等依赖注入框架。如果您的项目是新的,这仅适用于您自己的代码。

  • 对于那些难以进行单元测试的测试,您可以将 RemoteEJB 调用包装到它自己的接口中,这样可以更轻松地进行模拟。RemoteEJB 的测试将成为该项目的集成测试。

UserService 类不适合由其他“外部”服务(例如 OrderService)使用。它仅适用于 UserVxService。但将来,也许 OrderService 想要使用 UserService 中的一些代码

同一层上的服务之间相互通信没有任何问题。

在这种方法中,VO(如 UserV1VO)可以在我的无版本服务 (com.organization.api.services) 中使用,但这不可能发生。一个好的架构不允许出现不允许的事情。我有想法创建一个新项目,例如 api-services 并将 com.organization.api.services 放在那里以避免这种情况。这是一个好的解决方案吗?

仅仅因为您“可以”做某事并不意味着您应该做。虽然将层分离到它自己的项目中似乎是个好主意;实际上,没有什么可以阻止开发人员在该项目中重新创建相同的类,或者将 jar 包含在类路径中并使用相同的类。我并不是说拆分它是错误的,只是说它应该出于正确的原因而不是“假设情景”进行拆分。