code craft_冲动:“Craft.io设计”

code craft

大约半年前,我敦促您观看罗伯特·C·马丁(Robert C. Martin)的演讲《 建筑–迷失的岁月》 。 它主张采用一种设计,该设计应清楚地显示应用程序的域(例如,逻辑单元,服务,用例等),并在边上保留辅助方面(例如,交付机制,持久性等)。

虽然我真的很喜欢这个演讲,但它是理论上的(这不是一件坏事)。 现在,我找到了一个完美的副本: Sandro Mancuso的Crafted Design 。 他的问题陈述基本上是相同的,他的解决方案的核心概念也是如此。 但是他的演讲更为实际,并充满了具体的例子。

你应该看!

总览

像往常一样, 本系列文章不会重复整个讨论。 在这里,我将重点介绍所描述的体系结构,而忽略了导致该方法的大部分动机和思考过程,以及诸如如何测试这样的系统等有趣的关联主题。

这使我有机会填补我在帖子中留下的关于Martin演讲的空白,而我在相反的地方做了。

谈话

链接到vimeo

幻灯片在slideshare上显示 。 这篇文章中出现的所有图表均来自这些幻灯片。

一点动力

为了确保我们都在同一页面上(即使您没有回去看马丁的演讲或阅读我的帖子),我也会快速总结一下动机。

目标

当您查看项目的包结构时,应该立即可以看到以下内容:

  • 应用程序是关于什么的? 主要概念是什么?
  • 该应用程序做什么? 主要特点是什么?
  • 我需要在哪里更改? 我在哪里放置新功能?

但是,根据某些功能关系,您经常会看到控制器,存储库,帮助程序……或任意结构。 无论如何,都与域无关。

模型视图控制器

一个常见的来源是交付机制(例如,Web UI或REST接口)定义了结构。

例如,使用MVC,许多自以为是的框架(或仅仅是开发人员的习惯)都根据模式来组织源代码。 这导致代码结构完全不反映应用程序的域。

Mancuso继续讨论了由于将MVC过度用作项目的基本结构而引起的更多问题。

它有什么作用? 我不知道。
但这是一个网络应用程序! 那很重要!

精心设计

传递机制和域之间应该有明显的区别。 前者很可能会采用MVC,其中视图和控制器是框架要求它们采用的任何样式。 但是模型必须是域的自足表示。

这个模型是其余讨论的重点。

域驱动设计中的模型

该模型将是域驱动设计中定义的域模型。 它包含域的状态(主要以实体的形式)和行为(以不同的服务的形式)。

该模型所需的任何基础结构也在此处实现。 但是它本身是可以识别的,并且没有与领域概念混合在一起以保持模型不变。 (例如,“发送电子邮件”是一项服务,但其实现是基础架构。)

这使该模型成为构建应用程序所要解决的问题的可运行独立解决方案。

职责范围

系统不同部分的职责是明确定义的:

精心设计的责任

  • 控制器

    控制器存在于模型之外,并且可以通过不同的机制(例如REST)与其交互。 当他们需要某种事情发生时,他们会调用一个用例(有时甚至不止一个)。

  • 用例

    用例描述了应用程序执行的操作(例如“创建作者”)。 它们暴露于外界,并且是领域模型的切入点。

  • 域服务

    域服务与一个领域概念(例如“作者”或“书”)相关,它是该领域的入口点。 为了完成其任务,该服务可以与其他服务对话。

  • 基础设施服务,存储库,...

    所有其他实例都受一个域概念的约束,并且从未暴露给外界。 唯一的例外是在整个域中使用的域实体。

    这意味着,例如,面向作者的域服务将永远不会与图书存储库交谈以获取某些图书; 相反,它将称为“获取图书服务”。

  • 基础架构实施

    虽然基础结构服务,存储库等的接口是域模型的一部分,但它们的实现通常被认为是基础结构。

    因此,尽管在域模型中将“授权用户服务”定义为基础结构服务,但可以在其他基础结构实现中找到其实现(例如OAuth)。

应该考虑命令查询责任分离来构建用例和服务。 在这里,各个实例要么负责执行命令,要么负责查询和返回信息。

名字

快速绕过名称... Mancuso说,他尽力避免使用建筑概念作为名称。

因此,例如对于存储库而不是UserRepositoryBookRepository,他将分别选择UsersLibrary

储存库

存储库类似于数据访问对象 。 但是,尽管前者更加以数据为中心并公开了CRUD操作 ,但后者却显示为具有有用查询方法的集合。

根据上面讨论的职责,实体和存储库由域概念分开。 这在使用复杂的领域模型时会有所帮助,该领域模型通常由相似的复杂且交织的实体图表示。 问题通常来自于具有非常不同需求的不同查询(例如,应加载什么,是否应使用延迟加载,分页等等),但它们映射到同一固定实体图。 所提出的方法将关系分开,以使没有实体引用来自不同概念的其他实体。

精心设计的存储库

这足以写入数据库,因为一次只处理一个概念(例如“插入评论”或“更新评级”)非常普遍。 因此,可以通过与域服务和所涉及实体协作的单个用例来管理此类命令。

但是,从数据库中读取数据通常更为复杂,在该数据库中通常需要合并来自不同域概念的信息。 Mancuso建议不要使用域模型并尝试将所有请求的实体放在一起,而建议使用自定义查询,该查询创建具有正确数据的非规范化对象。 这使代码具有很强的表达力(因为对于该单个用例而言,查询和代表结果的对象才存在),并保持域模型的整洁。 他称这是一条捷径

结构体

使用此项目的模块/源文件夹/包(取决于您在做什么)的结构可以如下:

精心设计的存储库

  • 核心

    针对构建应用程序的问题的独立解决方案。

    • 基础设施

      模型所需的基础结构。

    • 模型

      域模型,即所有实体和服务。

    • 用例

      各个类中的用例。

  • 交货

    包含项目交付使用的任何内容,例如桌面UI或Web集成。 项目的这一部分可以根据框架的需求进行组织。

幻灯片包含更多实际文件夹结构的屏幕截图(从幻灯片22开始)。

核心

文件夹模型的内容清楚地显示了应用程序的用途–名称通常由名词组成。 该文件夹通常将包含子文件夹(例如book ),其中将包含与域概念相关的所有类(实体,服务,存储库;不要记住基础结构)。

用例的内容清楚地表明了应用程序的用途-名称通常由动词组成。 用例可以组织为子文件夹,这些子文件夹可能与史诗或用户故事有关。

基础结构将包含“所有外部内容”,这不是域的一部分。 这些类通常将实现模型中定义的接口。

交货

通过这种结构,可以解决MVC的典型问题。 例如,控制器不再包含业务逻辑。 相反,它们仅执行应有的操作:控制流程。 他们从视图中查询,调用正确的用例,获取返回的数据并使用它来更新视图。 所有这些都是几行,因此不再需要胖控制器。

从抽象流向特定流

在描述了这种架构方法之后,Mancuso对从抽象到特定行为的流程进行了观察。

所有交互都从用例的一些输入开始。 它们委托给服务,这些服务进入域以修改或查询实体或创建一些输出。 因此,虽然更靠近输入的实例主要负责控制流程,但更靠近输出的实例则在执行实际工作。 同时,前者采用更高级别的抽象,而后者则实现非常特定的行为。 精心设计的流程

在右图中,这表示从左(输入)到右(输出)的控制流。 越靠左,概念越抽象,流量控制就越多。 距离右侧越近,概念越抽象,行为越具体。

测试及更多

谈话中有很多内容,我不会讨论。

一个解释了如何使用不同的测试样式来验证系统不同部分的行为。 如果您对此感兴趣,请查看! (从29:53开始。)

另外,Mancuso像一把热刀一样穿过黄油切过他的幻灯片,因此几乎有20分钟的问题。 它们涉及安全性,模块化,事件源以及更多有趣的答案。 再次:检查一下! (从36:57开始。)

反射

我们已经看到精心设计如何将代码库结构化为模块/文件夹/包,以清楚地显示应用程序的主要概念和功能。

为此,应采用域驱动设计来创建域实体和服务(概念)。 用例(功能)被实现为单独的类。 它们是进入系统并协调其行为的入口。

然后,项目核心的顶级文件夹应包含这两个子文件夹以及一个用于基础结构实现的子文件夹(将其分开以保持域模型的清洁)。

交付机制(无论是丰富的桌面UI,Web UI和/或REST界面)应隔离到其自己的项目/模块/文件夹中。 可以根据最适合的结构进行组织。 但是重要的是保持其控制器苗条! 他们绝不应该只调用一个,可能是两个用例,并根据调用的结果更新视图。

翻译自: https://www.javacodegeeks.com/2015/05/impulse-crafted-design.html

code craft

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SELECT bs.report_no, bs.sample_id, bs.test_id, bs.service_type, bs.sample_name, bs.total_fee, bs.receivable_fee, bs.sample_no, bs.ranges, bs.grade, bs.sample_remark AS remark, bs.factory, bs.item_name, bs.apply_dept, bs.specification, bs.factory_number, bs.calibrat_point, bs.mandatory_flag, bs.inspection_type, bs.report_org_name, bs.plan_complete_date, bs.standard_instrument_name, bs.bleeding_site_name, bs.arrive_date, DATEDIFF( bs.plan_complete_date, NOW()) AS surplus_days, bs.order_no, bs.order_type, bs.customer_name, bs.order_id, bs.business_type, bs.group_id, bs.group_name, bs.item_id, bs.is_merge, bs.pass_time, bs.audit_time, bs.report_id, bs.compile_time, bs.generate_time, bs.pass_user_name, bs.audit_user_name, bs.compile_user_name, bs.report_state, bs.is_just_certificate, bs.label_price, bs.labor_cost, bs.product_type, bs.batch_number, bs.original_number, bs.type_no, bs.template_id, bs.template_version, bs.standard_instrument_id, bs.standard_instrument_name, bs.report_query_code, bs.test_user_id, bs.test_user_name, bs.test_time, bs.review_user_id, bs.review_user_name, bs.review_time, bs.or_number, bs.test_result, bs.test_result_text, bs.test_date, bs.test_address, bs.result_value, bs.unit, bs.test_dept_id, bs.test_dept_name, bs.sample_mass, bs.form, bs.color, bs.clarity, bs.amplification_detection, bs.precious_metal, bs.remarks, bs.photo, bs.identifying_code, bs.diamond_quality, bs.hand_ring, bs.craft, bs.instrument_photo, bs.customer_item_basis, bs.quality_photo, bs.check_point, bs.check_code, bs.mass_unit, bs.manufacturer_name, bs.manufacturer_addr, bs.result_sample_describe AS sampleDescribe, bs.test_rule AS metalRuleIdsStr, bsa.attach_id FROM view_sample_info bs JOIN bus_sample_report bsr ON bs.report_id = bsr.id JOIN bus_sample sa ON bsr.sample_id = sa.id JOIN bus_sample_attr bsa ON sa.id = bsa.id 需要按照bs.report_no 的整数来从小到大进行排序
07-15
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值