Here are a few guides for common patterns with ZIO: 使用模块和层: 如何借助ZIO环境构建大型的 ZIO 程序。 Test effects: 如何使用 ZIO Test 无缝测试效果化的程序. Mock services: 如何使用模拟(mocks)来测试服务之间的交互。 Handle errors: 如何处理 ZIO 中的错误(可声明的错误与无法预见的缺陷)。 Access system information: 如何使用ZIO访问环境变量和其他系统属性。
用 ZLayer 注入 ZIO 环境 ZIO 是围绕3个参数设计的,R, E, A。R 代表运行 effect 时的要求,这意味着我们需要满足这些要求才能使 effect 可运行。我们将探讨我们可以用 R 做些什么,因为 R 在 ZIO 中起着至关重要的作用。 有关 ZIO 环境的简单案例 让我们构建一个用户管理的简单程序,该程序可以检索用户(如果存在)和创建用户。我们需要一个 DBConnection 来访问数据库,并且程序中的每个步骤都通过环境类型来表示这一点。然后,我们可以通过 flatMap 将两个(小的)步骤组合在一起,或者通过 for comprehension 更方便地进行组合。 结果是一个决于对 DBConnection 的依赖的程序。 要运行该程序,我们必须通过 provide 方法提供 DBConnection,然后再将运算提供给 ZIO 运行时。 请注意,通过 provide 为该环境提供 effect 的行为导致的结果是消除了返回的 effect 中的环境依赖,返回的 effect 中的环境类型呈现为 Any。 通常,我们不仅需要数据库连接。我们需要使我们能够执行不同操作的组件,并且需要将它们连接在一起。这也是模块化的作用。 我们的第一个 ZIO 模块 接下来,我们将看到如何定义模块,并使用它们来创建彼此依赖的不同的应用层。核心思想是,一个层依赖于紧接在下面的层,但是完全不知道其内部实现。 这种模块式的表述是 ZIO 管理应用程序组件之间的依赖关系的方式,在组合性方面提供了强大的功能,并提供了轻松更改不同实现的功能。这在测试/模拟阶段特别有用。 模块是什么? 模块是仅处理一个关注点的一组功能的集合。限制模块的范围可以提高我们理解代码的能力,因为我们一次只需要专注于一个主题,而不会在脑海中纠缠过多的概念。 ZIO 本身就通过模块来提供基本功能,比如查看 ZEnv 是如何定义的。 模块的构成 让我们按照以下简单步骤构建一个用于用户数据访问的模块: 定义一个指定模块的名称的对象,它可以是(非必须)一个包对象。 在模块对象中定义一个特质(trait)服务,该服务定义了我们模块所公开的接口,在本例中它包括两个方法:检索和创建用户。 在模块对象中,通过 ZLayer 定义该模块的不同实现(有关 ZLayer 的详细信息,请参见下文) 定义类型别名,例如 type ModuleName = Has[Service](有关以下内容的详细信息,请参见下文) 我们遇到了两种新的数据类型 Has 和 ZLayer,接下来让我们熟悉它们。 Has 数据类型 Has[A] 表示对A类型服务的依赖。可以通过 ++ 运算符将两个 Has[_] 水平合并,如: 此时您可能会问:如果结果类型只是两个 trait 的混合,那有什么用?为什么我们不仅仅依靠特质 mixins? Has 赋予的额外能力是,通过将服务类型交叉映射到服务实现,从而得到的结果数据结构混合了每个实例,不仅可以访问/提取/修改其中的某个实例,同时也保证了彼此之间的类型安全。 根据之前的说明,为 Has[Service] 定义类型别名非常方便。通常我们不直接创建一个 Has,而是通过 ZLayer 来实现。 ZLayer 数据类型 ZLayer[-RIn, +E, +ROut <: Has[_]] 表示根据输入 RIn 来生成 ROut 类型的环境值,可能产生的错误类型用 E 来表达。 遵循环境的概念,当 RIn = Any 时表示可以不需要输入,并可将类型别名简写为 Layer。 创建 ZLayer 的方法有很多,这是一个不完整的列表: 用 ZLayer.succeed 或 ZIO.asService 通过现有服务创建 Layer ZLayer.succeedMany 根据一个或多个服务创建一个 Layer。 ZLayer.fromFunction 通过一个将请求转换成服务的函数创建 Layer。 ZLayer.fromEffect 将 ZIO effect 提升为一个具有效果化的环境的 Layer。 ZLayer.fromAcquireRelease 具有资源获取/释放能力的 Layer。这个想法与 ZManaged 相同。 ZLayer.fromServices 从多个服务中构建 Layer。 非常合理地,这些构建服务的方法各自还有不同的变体:具有效果化的(以后缀M来标示),资源化的(后缀Managed)或不同服务的组合(后缀为Many)。 我们可以水平地组合 layerA 和 layerB,通过 layerA ++ layerB 来组合两个层以构建出同时具有两个需求层的组合层, 我们也可以垂直地组合层,这意味着将一层的输出用作下一层的输入以构建出下一层,从而得到需要将第一层作为输入的第二层输出:layerA >>> layerB 将模块连接在一起 在这里,我们定义了一个模块来处理 User 域对象的 CRUD 操作。我们还提供模块在内存中的实现。 然后,我们定义另一个模块来执行一些基本的日志功能。我们提供了它的基于zioConsole 的 consoleLogger […]