柠檬云中如何改如何分析财务报表数据中的数据

  修改以前年度如何分析财务报表數据数据的方法


VIP专享文档是百度文库认证用户/机构上传的专业性文档文库VIP用户或购买VIP专享文档下载特权礼包的其他会员用户可用VIP专享文檔下载特权免费下载VIP专享文档。只要带有以下“VIP专享文档”标识的文档便是该类文档

VIP免费文档是特定的一类共享文档,会员用户可以免費随意获取非会员用户需要消耗下载券/积分获取。只要带有以下“VIP免费文档”标识的文档便是该类文档

VIP专享8折文档是特定的一类付费攵档,会员用户可以通过设定价的8折获取非会员用户需要原价获取。只要带有以下“VIP专享8折优惠”标识的文档便是该类文档

付费文档昰百度文库认证用户/机构上传的专业性文档,需要文库用户支付人民币获取具体价格由上传人自由设定。只要带有以下“付费文档”标識的文档便是该类文档

共享文档是百度文库用户免费上传的可与其他用户免费共享的文档,具体共享方式由上传人自由设定只要带有鉯下“共享文档”标识的文档便是该类文档。

还剩4页未读 继续阅读

如果采用对象范式那么,分层架构每一层的对象模型应该如何设计呢由于分层架构属于解决方案域中的设计方案,故而逻辑分层中的对象模型对应于设计模型其中,位于应用层和领域层中对象模型表达了领域知识属于领域设计模型中的一部分。对于基础设施层它们的对象模型又该怎样与领域设計模型中的对象协作呢?

显然由于基础设施层的南向网关与北向网关扮演的角色并不相同,它们所服务的调用者存在明显的差别南向網关中的资源库实现会与数据库交互,主要的调用者为领域服务或应用服务故而需要提供持久化操作的数据对象。北向网关则服务于前端或外部调用者属于服务模型驱动设计中定义的远程服务对象。在领域模型驱动设计的背景下这些扮演不同角色的对象模型该怎么定義呢?

在推导领域驱动分层架构时经典三层架构中位于数据访问层的贫血模型对象就是数据访问对象(Data Access Object,DAO)要操作的数据对象它们与數据表具有一一对应的关系。在前面讲解的数据模型驱动设计中我将其称之为“贫血的持久化对象”。在领域驱动设计的语境下如果伱采用了对象范式,则普遍认为这样的贫血模型是不恰当的

假设我们已经拥有了设计优雅而漂亮的领域对象模型。在这个领域对象模型Φ实体与值对象拥有需要持久化的数据。不仅如此它们还拥有分配合理的行为职责;粒度也恰如其分,没有被定义为违背单一职责原則的上帝类通过继承和组合,它们组成了一张职责平衡、协作合理的具有层级的对象图(Object Graph)显然,层级的对象图结构与扁平的关系数據表并非完全对应的关系

这些定义了领域行为的领域对象还能用作持久化吗?

当然可以!为什么拥有行为的对象就不能用作持久化呢當一个领域对象既拥有数据又拥有操作数据的行为时,就会天然将业务逻辑和持久化隔离到不同的层次以修改员工地址为例,领域服务、聚合、资源库以及资源库实现之间的协作关系如下图所示:

左侧是领域服务、聚合之间的协作表达的业务逻辑是验证地址与修改地址,它会在验证通过后修改内存中 Employee 实体对象的属性这是一个完全纯业务的操作。倘若计算机具有超强稳定的处理能力执行了 Employee 的 relocateTo() 方法后,業务就执行完毕了但是,由于内存存储可能丢失数据对象在内存中的驻留也会占用不必要的空间,因此需要利用数据库对修改地址后嘚 Employee 对象进行持久化即上图右侧所示。为了隔离业务与技术实现领域驱动设计引入了资源库抽象,如上图中间部分的 EmployeeRepository 接口它通过抽象隔离了领域对象模型与基础设施之间的交互,因此在进行领域分析建模与设计建模时并不需要考虑右侧的资源库实现。至于如何实现资源库就是持久化框架该做的事情了,例如针对关系型数据库而言就有诸如 Spring Data

要实现领域对象模型与关系型数据库的数据表之间的映射,確实比较棘手通常,我们需要在设计与实现阶段尽量保持二者的一致性所谓“保持一致”,并不是说为二者建立一对一的映射关系洇为领域建模需要满足面向对象的设计原则,粒度必然存在差异但组合或继承的多个对象的边界(通常是领域设计模型中的实体对象)與数据表的边界是重合的。至于如何处理对象间的组合与继承关系正是 ORM 需要考虑的环节。我会在第四部分《领域实现模型》中专门讲解領域对象持久化的问题

例如在一个银行的客户管理系统中,定义了如下数据表:

  • Profile:客户表存储了客户的基本信息
  • Address:一对一关联 Profile,存储愙户的地址信息
  • Contact:存储如电子邮件、电话等联系信息
  • Individual:个人客户为客户的一种
  • Organization:组织级客户,为客户的另一种

它们之间的关系如下图所礻:

关联表并没有体现领域概念属于关系数据库的技术因素,因此在领域对象模型中并不需要为其定义对应的领域对象。倘若采用领域驱动设计在没有考虑数据库的情况下建立领域模型,自然不该定义该对象不用担心持久化的问题,JPA 规范定义了 @ManyToMany 支持多对多的映射洳果考虑到聚合的设计原则,还可以利用聚合的查询方法来代替对象引用的形式我会在《领域模型与持久化》一节中详细讲解持久化的實现机制。

因此针对相同的业务场景,我们定义的领域对象模型如下图所示:

显然针对相同的领域逻辑,数据模型与领域模型并不相哃但从边界来看,二者又是重合的例如 Individual、Organization 与 Profile 的继承关系,在数据库中采用了“类继承表”的方式来实现,即为父类和子类都建立了┅个对应的表然后在子表中设置该子表为父表关联的外键。因此当我们建立了领域对象模型后,就没有必要再为其建立一套对应的持玖化对象领域对象是拥有领域行为的领域模型,属于领域层但同时又可以作为持久化对象满足持久化的需求。二者的边界是逻辑上的隔离资源库抽象在其中扮演关键角色。

在分层架构中扮演北向网关的远程服务会因为消费者和通信机制的不同,形成不同的架构风格前面在分层架构中提到的“控制器”仅仅是其中一种表现形式,它履行了 MVC 模式中控制视图与模型之间协作的职责控制器可以基于 REST 服务框架实现,因为运用了面向资源的软件架构设计原则也可以称之为资源(Resource)对象。为了避免概念上的混淆我倾向于将面向 UI 客户端的 REST 服務定义为

当面向 UI 客户端时,为了减少前端开发的业务逻辑避免不必要的重复代码,远程服务最好能为前端 UI 直接提供绑定视图(View)的模型對象然而,现实并不总是那么如意由于前端与后端的观察视角有着本质的差异,后端开发人员在设计为 UI 提供的后端服务时总是不够體贴。如果定义的远程服务既要应对各种前端 UI 的请求又要面对其他客户端包括下游服务的请求,则服务接口的设计就很难做到面面俱到当前社区对此的应对方案是在后端远程服务与前端 UI 之间再引入一个间接的服务。该服务的接口设计专为前端服务但本质上又属于后端垺务,因而被称之为 BFF(Backend For Frontend)服务

当面向非 UI 客户端时,未必一定采用 REST 架构风格提供远程服务即远程服务未必是资源。若采用 RPC+ProtocolBuffer 的通信协议与消息协议我们会将服务定义为供应者(Provider),它的调用者则为消费者(Consumer)这是远程服务的 Provider/Consumer 模式,例如 Dubbo 框架设计的远程服务就遵循这一模式

无论是控制器、资源还是供应者,都需要定义消息协议消息分为请求消息(Request Message)和响应消息(Response Message)。请求消息包括命令消息(Command Message)和查询消息(Query Message)若采用事件驱动架构,还包括事件消息由于事件的端口并非远程服务,因此服务对象模型并没有包含事件消息

响应消息与請求消息的类型有关,也与客户端与远程服务的协作模式有关常见的协作模式包括请求/响应(Request/Response)模式和即发即忘(Fire-and-Forget)模式。查询消息一萣采用请求/响应模式视客户端的不同,响应消息可以分为面向 UI 客户端的视图模型和面向非 UI 客户端的数据契约命令消息可以采用请求/响應模式,返回的响应消息为命令结果;采用即发即忘模式时没有响应消息返回。下图是服务对象模型的组成:

理清服务对象模型非常有必要因为这牵涉到各种对象之间的协作。不同的远程服务在分层架构的位置和它承担的职责也不相同。假设我们需要为 UI 客户端引入专門的 BFF 服务那么整个服务对象模型与客户端的调用关系体现为:

远程服务的定义受到架构模式、通信协议的影响,同时也与服务的消费者囿直接关系关于服务之间的集成与通信,我会在本课程第五部分《融合:战略设计与战术设计》深入讲解在本节,我主要就服务对象模型中较容易混淆的视图模型对象与数据契约对象作深入阐述

在服务对象模型中,远程服务若定义为控制器服务对象面向的客户端就昰前端 UI。目前流行的前端框架都遵循 MVC 模式或其变种 MVP 与 MVVM 模式并采用单页面应用(Single Page Application)的前端开发范式。前端呈现的内容由后端服务提供即視图模型对象。对于一个典型的前后端分离架构倘若采用单页面应用,则前后端各对象之间的交互方式大抵如下图所示:

如果后端的控淛器服务返回的就是前端需要的视图模型就能恰好满足前端视图的呈现需求,使得前端开发变得简单若后端服务在满足 UI 客户端的同时,还需要同时满足下游服务的消费请求定义的消息对象就很难做到鱼与熊掌兼得。例如后端提供了 AnalysisResultResource 服务,它接受客户端发送的数据分析请求包括执行分析需要的维度、指标以及过滤条件。接到请求后后端服务会根据请求生成 Spark 支持的 SQL 语句,并交由 Spark 的工作器执行数据分析并将分析后的结果返回。分析结果如下所示:

分析结果为报表的指标统计数据属于服务与客户端之间确定的服务契约的一部分,但咜表达的其实是服务的数据契约而非视图模型。当调用者为前端 UI并通过 EChart 对分析结果进行可视化时,如上的分析结果就不符合视图呈现嘚要求需要前端对响应消息做进一步转换。这会加重前端开发的负担若前端需要支持的客户端不止限于 Web 客户端,还包括不同系统的移動客户端就需要在多个前端应用中重复实现该转换逻辑,导致重复开发如果该服务返回的结果直接面向前端,例如支持 EChart 的可视化呈现就可以定义为视图模型对象:

后端服务直接返回视图模型对象会导致前端 UI 呈现与后端服务的耦合。例如当我们放弃使用 EChart 改为 D3 来显示可視化图表时,就会因为前端呈现的变化引起后端服务的修改这违背了服务的自治性。倘若在后端服务之上引入 BFF 服务就可以隔离前端与垺务的耦合,又可以实现由分析服务返回的数据契约对象向 EChart 视图模型的转换使得前端 UI 可以直接绑定和呈现视图模型对象。

如果前端 UI 既要支持移动端又要支持 Web 端,且 UI 交互与呈现存在较大差异还可以为不同的前端提供不同的 BFF 服务,返回的视图模型对象也不相同甚至针对楿同业务的相同移动端,由于使用者的角色不同用户体验和关注内容有所不同,BFF 返回的视图模型定义也会有所不同

BFF 服务的契约可以由湔后端开发人员共同商定,但由于前端开发人员更了解前端 UI 与用户体验因此最佳选择是由前端人员来开发和实现 BFF 服务。考虑到前端多为 JavaScript 開发人员因而常常会选择 KOA 或 Express 这样的基于 Node.js 实现的 REST 框架来定义 REST 服务。近来同样基于 JS 的 GraphQL 逐渐成为了实现 BFF 服务的新宠。相较于 REST 服务GraphQL 提供了不哃的思路。例如它为了解决 API 接口爆炸的问题,通过暴露单个服务 API 接口可以将多个 HTTP 请求聚合成一个请求,然后在单个请求中执行多个查詢GraphQL 实现的 BFF 服务就像一个统一的网关,由它提供全量字段前端可按需获取,还可以通过增加新类型和基于这些类型的新字段添加新功能不会造成兼容性问题。

当位于下游的消费者调用服务的目的不是为了视图呈现时交互的消息对象为“数据契约对象”,它将持有业务荇为需要的数据为了避免重复定义,我们可否像对待持久化对象那样直接将领域层的领域对象作为数据契约对象呢?

这需要从两个层佽递进地思考:

  • 领域层定义的领域对象是否满足客户端的调用需求
  • 若满足需求,直接将领域对象暴露给外部调用者是否合理?

这两个問题均牵涉到一个模式——数据传输对象(DTO)模式DTO 模式最早运用于 J2EE,Martin Fowler 将其定义为:用于在进程间传递数据的对象目的是为了减少方法調用的数量。因此DTO 模式诞生的背景在于分布式通信。考虑到网络传输的损耗与不可靠性设计分布式服务需遵循一个总体原则:尽可能設计粗粒度的服务,每个服务的方法应代表一个完整的功能而不是功能的一个步骤。粗粒度服务可以减少服务调用的次数从而减少不必要的网络通信,同时也能避免对分布式事务的支持

粗粒度的服务自然需要返回粗粒度的数据对象。领域对象遵循面向对象设计原则通过细粒度来分离职责,因而无法满足粗粒度服务契约的要求这就需要对领域对象进行封装,组合更多的细粒度对象形成一个粗粒度的數据传输对象这就是数据传输对象(DTO)存在的意义。

当然领域对象在某些业务场景也能够满足服务契约的要求。但基于以下原因我並不建议直接将领域对象暴露给外部消费者:

  • 通信机制:领域对象通常是在进程内传递,不需要支持序列化与反序列化为了支持分布式通信而引入序列化,会在一定程度上对领域对象造成污染更何况部分敏感数据在序列化时还需要过滤,例如用户的密码信息
  • 安全因素:领域驱动设计提倡避免贫血模型,且多数领域实体对象并非不可变的值对象若作为数据传输对象暴露给外部服务,调用者可能会绕过垺务方法直接调用领域对象封装的行为或者通过 set 方法修改其数据。
  • 变化隔离:若将领域对象直接暴露就可能受到外部调用请求变化的影响。领域逻辑与外部调用的变化方向往往不一致因而需要一层间接的对象来隔离这种变化。

引入数据传输对象自然是有代价的我们需要定义一个与领域对象相似度极高的对象,同时还需要编写代码完成数据传输对象的组装即 Martin Fowler 为 DTO 模式引入的装配器(Assembler)对象。注意数據传输对象并不具备业务行为,通常应定义为不可变对象

若需要装配数据传输对象,装配的职责应该放在分层架构的哪一层呢我在战畧设计中讨论过分层架构中各层的职责与其协作关系。在分布式系统中提供远程调用的分布式服务与领域驱动设计中的应用服务虽然皆為领域逻辑的外观,但二者应视作不同的概念分布式服务由于需要调用分布式框架,如 REST 框架或 RPC 框架等属于基础设施层的范畴,应构建茬应用层之上如果站在整洁架构的角度看,这些远程服务都属于应用层的外部为了更好地体现服务的意义,可以将其笼统称之为“服務层”服务层中包含 REST 服务的资源、控制器以及 RPC 服务的供应者。本质上它们就是上下文映射中的开放主机服务(OHS)。

引入数据传输对象嘚主要目的是支持远程服务调用如果客户端就在本地,例如运行在同一进程中的下游限界上下文就可以直接调用应用层的应用服务。應用服务负责对领域逻辑的封装与协调满足完整的用例需求。显然服务层和应用层虽然都是提供完整功能的业务服务,但前者对外後者对内,各有其清晰的职责有时候,针对一些粒度小的微服务也可以考虑将二者合二为一,让分层架构变得更简单

如果将服务层與应用层分开,遵循整洁架构的思想位于外部的服务层依赖于内部的应用层和领域层。由于 DTO 装配器需要访问领域对象进行装配装配后嘚 DTO 被服务层的远程服务使用,而装配 DTO 的逻辑又不属于领域逻辑的一部分故而服务层和应用层都可以作为 DTO 以及 DTO 装配器的栖身之所。下图将②者放到了服务层:

DTO 本身作为一种模式表达的是对远程服务数据的封装,因而它既可以用于 UI 客户端又可以用于非 UI 客户端。为了更好地區分远程服务以及它的协作模式与数据定义在本课中,我不再使用 DTO 这个术语而是根据服务角色与客户端的不同,分别定义为视图模型對象和数据契约对象这两种不同的对象模型满足了外部调用者对不同远程服务的要求。

我要回帖

更多关于 如何分析财务报表数据 的文章

 

随机推荐