https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html
我对这种模式有一些疑问.数据库处于更高层,但实际上如何运作?例如,如果我有一个微服务,只是管理这个实体:
person{
id,
name,
age
}Run Code Online (Sandbox Code Playgroud)
其中一个用例是管理人员.管理人员正在保存/检索/ ..人员(=> CRUD操作),但要做到这一点,Usecase需要与数据库通信.但这将违反依赖性规则
使该体系结构工作的首要规则是依赖规则.此规则表明源代码依赖性只能指向内部.
如果我得到一个GET /person/{id}请求我的微服务应该像这样处理吗?
但使用依赖倒置将是违反
内圈中的任何东西都不能知道外圈中的某些东西.特别是,内圈中的代码不得提及在外圈中声明的内容的名称.这包括功能,类.变量或任何其他命名的软件实体.
跨越边界.在图的右下方是我们如何穿过圆边界的示例.它显示控制器和演示者与下一层中的用例通信.注意控制流程.它从控制器开始,在用例中移动,然后在演示者中执行.还要注意源代码依赖性.他们中的每一个都指向用例.
我们通常使用依赖性倒置原则来解决这个明显的矛盾.例如,在像Java这样的语言中,我们将安排接口和继承关系,使得源代码依赖性反对跨越边界的正确点处的控制流.
例如,考虑用例需要调用演示者.但是,此调用不能是直接的,因为这会违反依赖关系规则:内圈中不能提及外圈中的名称.因此我们在内部圆圈中使用用例调用接口(此处显示为用例输出端口),并使外部圆圈中的演示者实现它.
相同的技术用于跨越体系结构中的所有边界.我们利用动态多态来创建反对控制流的源代码依赖关系,这样无论控制流向何种方向,我们都可以符合依赖关系规则.
Use Case层是否应声明将由DB Package(框架和驱动程序层)实现的存储库接口
如果服务器收到一个GET /persons/1请求,PersonRest会创建一个PersonRepository,并将这个Repository + ID传递给ManagePerson :: getPerson函数,getPerson不知道PersonRepository但知道它实现的接口,所以它不违反任何规则吗?ManagePerson :: getPerson会使用该Repository来查找实体,并将Person Entity返回给PersonRest :: get,它会将Json Objekt返回给客户端吗?
遗憾的是英语不是我的母语,所以我希望你们能让我知道我是否理解了这种模式是正确的,也许可以回答我的一些问题.
Ty提前
最近,我找到了鲍勃叔叔的The Clean Architecture帖子。但是当我尝试将它应用到当前项目时,当一个用例需要依赖另一个用例时,我陷入了困境。
例如,我的领域模型是目标和任务。一个目标可以有多个任务。当我更新一个 Task 时,它需要更新其父 Goal 的信息。换句话说,用UpdateTask例将用UpdateGoal例作为依赖项。我不确定这是否可以接受,或者我们是否应该避免用例级别的依赖关系。
我正在按照Microsoft 架构指南创建 ASP.NET Core Web 应用程序。
该指南实现了非常简单的简洁架构模式。
如果您查看使用干净架构模式的示例项目,您将看到有一个包含 ApplicationUser.cs 类的Infrastructure/Identity文件夹。
我的问题:
我正在使用实体框架,ApplicationCore 类库中的一个业务实体需要包含一个 ApplicationUser 列表。ApplicationCore 库不应引用任何其他项目。它包含所有接口和业务实体。如何在我的基础设施/身份项目中保留 ApplicationUser 类,并且在不违反规则的情况下仍然在 ApplicationCore 项目中的业务实体之一中使用它。
我知道一种解决方案是不在我的基础设施项目中存储 ApplicationUser 实体。但是,我觉得它应该在那里,因为它在实现 IdentityUser 时将始终依赖于 Identity。
c# entity-framework-core asp.net-core-mvc asp.net-core clean-architecture
什么是清洁架构背景下的用例和交互器。我读到用例包含业务逻辑,但是如果有一个表示层,我们可以在演示器中放置业务逻辑,那么为什么要用例呢?
我是 flutter/dart 的新手,我正在尝试使用干净的架构设计创建一个小应用程序。在开始编写代码以充分利用它之前,我阅读了 Uncle Bob 的干净架构的一些博客和一些演示,现在是时候实现它了。
我想我的应用程序可以分为 3 个主要功能:
我开始按照干净的模式实现身份验证功能,也就是说使用域、数据和表示层,我想我做得很好。它(几乎)已经经过全面测试(我正在尝试进行一些 TDD),并且似乎可以按我想要的方式工作。
现在问题来了。我想实现类功能。我希望它可以独立于身份验证,但事实并非如此......类功能需要从身份验证功能中获取经过身份验证的用户。我在互联网上搜索了很多,但我找不到如何实现具有需要共享一些数据的多个功能的干净架构。
所以我有两个问题:
预先感谢您的回答。
我正在使用 Kotlin 开发一个 Android 应用程序,我需要在其中获取移动设备的当前位置。我已经在各种示例中找到了一种方法,但我不知道如何根据 Clean Architecture 与 MVVM 集成此逻辑。
在我的架构中,我有以下几层:表示层、用例层、数据层、域层和框架层。我用 MVVM 模式组织了表示层。我还使用 Koin 进行依赖注入。
我从框架层中的数据源获取应用程序所需的所有数据。例如,远程获取的数据或从数据库获取的数据,或设备(位置)提供的数据。
以下是从 ViewModel 获取位置所涉及的文件示例:
ConfigurationViewModel(表示层):
class ConfigurationViewModel(private val useCase: GetLocationUseCase) : ViewModel() {
fun onSearchLocationButtonClicked() = liveData<Resource<Location>>(Dispatchers.IO) {
emit(Resource.loading())
try {
emit(Resource.success(data = useCase.invoke(UseCase.None())))
} catch (exception: Exception) {
emit(Resource.error(message = exception.message))
}
}
Run Code Online (Sandbox Code Playgroud)
GetLocationUseCase(用例层):
class GetLocationUseCase(private val locationRepository: LocationRepository) :
UseCase<Location, UseCase.None>() {
override suspend fun invoke(params: None): Location = locationRepository.getLocation()
}
Run Code Online (Sandbox Code Playgroud)
LocationRepositoryImpl(数据层):
class LocationRepositoryImpl(private val locationDeviceDataSource: LocationDeviceDataSource) :
LocationRepository {
override suspend fun …Run Code Online (Sandbox Code Playgroud) (我编写了 MobX 包作为示例,但它可以是 BLoC、Redux 之类的任何东西......)
我可能对该图有疑问,因为 Store 驻留在表示层中并负责更改状态。
想象一下,应用程序通过 TodosStore 中名为“getTodos”implementend 的方法加载待办事项列表。“getTodos”的实现可能是:
_state = State.loading();
final result = GetTodos();
_state = State.done();
Run Code Online (Sandbox Code Playgroud)
(我把事情过于简单化了)
它首先将状态更新为加载,调用返回列表的用例并将状态设置为完成。
我在这里发现的问题:
这是一个相当简单的例子。但是让我们想象一下,我有一个包含 3 个不同数据列表的视图(这可能是一个荒谬的例子)。该视图与 3 个不同的商店交互。应用栏中有一个按钮。其目标是清除 3 个列表。
如何实现这种行为?
问题是这个视图并不像我们想要的那么愚蠢。商店甚至不与任何用例交互
ClearLists 用例在这里应该合法吗?
每个视图都有自己的 ViewModel。VM 只是与用例交互。这些 UC 可能返回值,也可能不返回值。例如:我的 UC 是 ValidateNumberRange,我可能不会与商店交互。返回一个布尔值是有意义的。但如果我的 UC 是 ClearTodoList,它可能会与商店交互。成功或失败可能是一个存储值。这样返回值可能没有用。
通过这个新图表,“GetTodos”用例可调用方法实现可能是:
store.set(State.loading());
final result = repo.getTodos();
result.fold(
(failureMsg) { store.set(State.failure(failureMsg)); },
(newList) { store.set(State.done(newList)); },
); …Run Code Online (Sandbox Code Playgroud) 我和我的同事正在争论哪里才是将实体对象或远程 dto 对象映射到简单域对象的正确位置。
我们的结构看起来像这样。
源(包括 dao)> 存储库(包括源)> 用例(包括存储库)
我的同事认为映射到域应该在源内部完成,以便域对象可以传递到下一层,如下所示
class SomeSourceImpl(private val dao: Dao) : SomeSource {
override fun get(): Observable<DomainModel> {
return dao.getResponse().map { it.mapToDomain() }
}
}
Run Code Online (Sandbox Code Playgroud)
我的同事认为,根据鲍勃叔叔的说法,这是由于依赖规则造成的。
该规则规定源代码依赖关系只能指向内部。内圈中的任何事物都无法了解外圈中的任何事物。特别是,在内圈中的代码中不得提及外圈中声明的内容的名称。这包括函数、类。变量,或任何其他命名的软件实体。
我非常不同意直接映射到源内部域的方法,因为这样存储库就会变得贫乏,我们因此采用贫乏存储库无用的反模式,它们所做的就是盲目传播来自源的所有内容。(现在你可能会说源也很贫乏,我们可以简单地删除它们并将 dao 对象直接包含到存储库中,但这在我们的例子中是不可能的)。
相反,我建议源返回原始数据库实体(如果我们进行休息调用,则返回远程实体),因为源返回原始数据以供以后处理是有意义的。存储库的工作是从源获取结果,然后将其映射到域,最后将此域对象传播到类似这样的用例。
class SomeRepoImpl(private val someSource: SomeSource) : SomeRepo {
override fun get(haId: String): Observable<DomainModel> {
return otherAssetSource.get().map { it.mapToDomain() }
}
Run Code Online (Sandbox Code Playgroud)
我还在 github 上发现了一些示例,它们映射到存储库内的域而不是源
这也是 iOS 版的
关于可以将实体映射到域对象的位置,干净架构原则中的严格规则是什么?
在清洁架构中,我们将用例作为业务逻辑规则。但我们也可以直接调用存储库中的函数,因此我们不需要用例。这背后的原因是什么?
示例用例
class GetMarketUseCase implements UseCase<Stream<ResponseModel>, void> {
final PriceTrackerRepository priceTrackerRepository;
GetMarketUseCase(this.priceTrackerRepository);
@override
Stream<ResponseModel> call(void params) {
return priceTrackerRepository.getMarketWithSymbols();
}
}
Run Code Online (Sandbox Code Playgroud)
样本库
class PriceTrackerRepositoryImpl implements PriceTrackerRepository {
late final PriceTrackerDataSource priceTrackerDataSource;
PriceTrackerRepositoryImpl(this.priceTrackerDataSource);
@override
Stream<ResponseModel> getMarketWithSymbols() {
return _marketStreamController.stream;
}
Run Code Online (Sandbox Code Playgroud) Service(或BroadcastReceiver等..) 属于干净架构包结构中的哪里?我想知道域和数据层之间...我会创建一个名为servicesin domainpackage.json 的新包。是这样吗,还是我应该采取其他方式?