【问题标题】:Can one controller call multiple usecase (interactor) in Clean Architecture?一个控制器可以在 Clean Architecture 中调用多个用例(交互器)吗?
【发布时间】:2020-10-10 01:23:11
【问题描述】:

我正在设计一个带有 spring 后端的移动应用程序。

在我的移动应用程序中,我有一个随机播放按钮。当用户单击按钮时,我会从 GPS 获取用户的当前位置并发送请求,然后使用此位置信息查找当前用户附近的用户。

顺便说一下,使用相同的用例对用例进行随机播放和过滤,唯一的区别是当随机播放我应用默认过滤器时,比如所有年龄、所有距离等。

~~ 随机/过滤请求 ~~

{  
  interestedGenders: ["WOMAN"]  
  minDistance: 1,  
  maxDistance: 100,  
  minAge: 18,  
  maxAge: 65,  
  latitude: 31.4,  
  longitude: 27.1  
}

好的,现在我的问题是我不想实时跟踪/更新用户位置,所以我想当用户随机播放(或过滤)时我已经有了用户位置,所以首先我更新数据库中的用户位置然后我使用这个位置信息使用其他过滤器(性别、年龄等)查找附近的用户

我在 mongo db 中有 3 个简单的集合(帐户、配置文件和 userLocations)

~~个人资料文件(简体)~~

{  
  id: "xxx",  
  accountId: "yyy",  
  gender: "MAN",  
  interestedGenders: ["WOMAN"]  
}

~~用户位置文件~~

{  
  id: "zzz"  
  accountId: "yyy",  
  location: {  type: "Point",  coordinates: [31.4, 27.1]  }  
  lastUpdated: "2020-10-10T00:59:37.154Z"  
} 

这是我使用的代码。首先我执行更新用户位置用例。如果用户在数据库中没有记录,则更新位置用例创建记录,否则更新用户位置。然后我发现用户符合条件。

@ApiController
@AllArgsConstructor
public class FilterProfilesController {

    private final UpdateUserLocationUseCase updateUserLocationUseCase;
    private final FilterProfilesUseCase filterProfilesUseCase;

    @PostMapping("/profiles/filter")
    public ResponseEntity<BaseResponse> filterProfiles(@AuthenticationPrincipal AccountId accountId, @RequestBody FilterProfilesRequest request) {
        var userLocation = new Location(new Latitude(request.getLatitude()), new Longitude(request.getLongitude()));

        updateUserLocationUseCase.execute(new UpdateUserLocationUseCase.Command(accountId, userLocation), () -> {
        });
        
        // Conversions from request to value objects (simplified)
        
        var query = new FilterProfilesUseCase.Query(accountId, userLocation, interestedGenders, ageRange, distanceRange);
        var presenter = new FilterProfilesPresenter();

        filterProfilesUseCase.execute(query, presenter);

        return presenter.getViewModel();
    }
}

设计这个的最佳方法是什么?
如何解耦更新位置用例(来自 FilterProfilesController)但在过滤用例之前仍然调用?
我应该写一个自定义方面还是什么?

一些问题的答案

:为什么我没有将用户位置信息放入个人资料集合中?

A:因为我们在用户注册时没有得到这个信息。所以位置字段保持为空,直到用户使用随机播放或过滤页面并且我在 mongo db 中使用地理空间查询,所以我猜这可能会导致错误。
加上配置文件对象已经有很多字段,我认为分离用户位置为未来的用例提供了很大的灵活性。

【问题讨论】:

    标签: spring spring-boot architecture domain-driven-design clean-architecture


    【解决方案1】:

    通常建议您将应用程序的“写入”端与“读取”端分开。

    您可能希望独立扩展查询服务,因为写入与读取不成比例。对于每次写入(GPS 位置),您可能需要执行 10 倍或 100 倍的查询。

    在您的示例情况下,您可能会第一次收到 GPS 位置,但允许用户使用不同的过滤器组合执行多个查询。假设您自动收集用户的 GPS 位置,它可能不会在查询之间改变。

    您的应用程序的域模型通常不适合查询。您通常会构建相同数据的多个表示(写入端),以满足不同 API(读取端)所需的响应格式。您可以结合使用Domain EventsSubscribers 来做到这一点,甚至可以单独为查询端创建一个单独的微服务。

    因此,最好将GPS Location Update 用例与查询分开。这会导致两次调用,但作为回报,您会获得很大的灵活性。

    因此,最好让调用者(本例中为 UI)在请求数据之前先执行更新。这还有一个额外的好处,即您可以异步收集 GPS 更新(作为 ping)来更新用户位置,而不仅仅是在用户请求数据时。

    【讨论】:

    • 嘿@Subhash 感谢您的回答。我像你说的那样分离了 api 调用。
    • 顺便问一下,我能得到您对自定义读取模型的意见吗?例如,有时我们需要使用 2-3 个不同的聚合来准备查询结果。你如何处理这种情况。因为存储库必须返回聚合,但是例如,如果我想获取距离的配置文件,我需要使用 2 个不同的集合,那么我应该在存储库中返回什么?
    • 域模型和所有相关概念(如聚合、有界上下文、存储库)仅处理应用程序的写入端。查询端和读取模型独立于您的域工作。
    • 读取模型在后台填充,不受聚合边界的约束。根据 API 等消费者的要求,相同的核心数据结构可以有多个读取模型。如果您想使用存储库模式,我建议您创建一个不映射到您的域模型的不同存储库。
    猜你喜欢
    • 2016-10-02
    • 2021-06-16
    • 1970-01-01
    • 1970-01-01
    • 2012-03-06
    • 2017-09-30
    • 1970-01-01
    • 2021-08-09
    • 1970-01-01
    相关资源
    最近更新 更多