这是 Martin Fowler 在《企业应用架构模式》之前出的一本二十多年前的书,中译本是2020年4月才出版,由与 Martin Fowler 在同一家公司 ThoughtWorks 的钟敬翻译。我在团队内部向小伙伴们介绍架构方面知识时,提起ThoughtWorks 这家公司,一直将其评价为软件界的圣殿。
信息系统的架构,抛开单机模式不谈,从C/S模式到B/S模式的架构迁移,大致就是这二三十年间软件开发领域发展轨迹的一个总体轮廓。在这背后驱动的,很可能离不开以 Martin Fowler 《企业应用架构模式》为代表提炼出来的三层架构和 MVC模式等。

(我画的一个整合模型图)
而相对于ThoughtWorks这些专业软件企业,另外那些互联网大厂,因为应用的体量巨大,解决这类问题的动机导致他们给软件行业最大的贡献,可能仅在于扩展了这些架构模型中的层级,在软件工程层面贡献甚少。以云计算中涉及的存储为例,见下图:

(来源网络,找的这个图可能不太精准,主要是表达这种层次化架构视图,不断出现的新的中间件,就是这种层次化层级增加的表现)
所以我们的互联网公司的软件工程师们,因为这些层级的不断加深,被迫要更不断的新手上掌握的工具,以保持与行业的同步。可是,从软件工程过程的视角,这些工具主要是”实现“阶段的事务,也就意味着逐步的软件工程过程的其它阶段如需求分析、设计,已经快要无力深入,终于只能托付给其他专人,于是造成进一步分工(某种说法叫”残化“),沦落(某种程度上的现实)为”码农“。

(国标中定义的软件生命周期八个阶段示意图)
《分析模式》这本书重点探讨了在软件开发活动中,从软件工程过程视角,”分析“阶段的模式总结,及将模式模型从”分析“阶段到”设计“阶段转换的方法论。所以这本书主要面向分析、设计、实现三个阶段中处于分析阶段的分析师。这类书籍,要求读者拥有一定的抽象思维能力,有那种“形而上”的逻辑学或哲学意味,所以如果感觉读不下去的时候,可以停下来,从头开始读,给脑子更多联想和想象的提取机会,必能有所助益。开卷有益,开一遍不行,就开第二遍,第三遍。
当然,我个人观念中理想的软件工程师是对应于三个阶段所谓分析师、设计师、码农的结合体,是为”全栈工程师“。《分析模式》中讲到分析模型与概念模型、领域模型指代的是同一种内涵。以个人肤浅的理解,DDD领域驱动的开发,某种程度上就是这种”全栈“导向的一种趋势和方案,那我们就要从学会分析系列现象问题所涌现出来的模式(Pattern,用另一种译法”斑图“理解起来更为直观)开始。
书中的给出的第一大模式是责任模式(accountability),这是所有涉及到业务关系梳理时,绕不过去的第一道坎。这个模式是从组织建模问题引申的出来。当我们做企业应用系统分析时,首先要就梳理企业的组织架构,我们在职场所常见的那种组织架构图,往往也是五花八门的,而站在”稍高一点“视角(《用系统工作》)的模式定义,要兼顾当下的易用性与未来的灵活性,最低标准是识别出组织层级化模式,更高标准是识别出分离组织、组织结构、组织结构类型的模式,再高标准,就是更抽象通用的责任系列模式。
简单举例来说,一家全国性集团企业,一般有总部、各”子公司/经营单位(operating unit)”、往下按“业务分片/地区(region)”、再往下”分公司/分部(division)“、最后“网点/办事处(branch/office)”,如果在见到如此确定的组织业务结构的时候,我们第一反应一定是分别建立这四个模型,因为这太明显了。

(独立的四个模型)
可是,分析模式要求我们站在比当前所在“稍高一点”的视角,感受和分析那种可能性变化“涌现”后的情形,进行分析和识别。然后就会发现这种由四个独立模型在业务结构变化时比如去掉“地区”,就需要整体改变模型之间的关系,进而要求我们不得不提取出一种更灵活一点的模型如组织层级模型,将组织和组织结构合并为一个模型,通过层级属性来表达组织结构的约束关系。

(组织内部的层级关系通过一个层级属性来表示)
如果再往下分析更多可能性涌现,就会发现这种组织层级模型,主要是假定组织只存在一种结构关系,当要定义多种结构关系时,便不够灵活,于是不得不将组织结构关系/类型从组织的属性中独立出来,以便支持多对多的场景。

(将组织和关系分离后,组织结构为组织与组织结构类型之间的关系)
这时,我们发现我们分析的对象就变成了“组织”和组织之间的结构关系/类型,“组织结构”是关系的产物,关系背后的约束规则以及随着规则变化形成的历史记录,可以进一步再建模。

(组织结构成为了一种业务流程数据,需要记录业务流程轨迹,而非仅是我们常规理解的基础数据)
进而,一旦涉及到业务流程,从“稍高一点”的视角,业务的各参与方就存在“责任”,责任可能会发生变更,同时在兑现“责任”的过程中还产生了相应的活动。

(责任模式下的实体模型)
进而,通过资深领域专家的分析,不断的识别涌现出来的模式,用一种模式解决对应的一系列所有同样问题,后续在软件工程过程中以这些模式为沟通语言,形成的是一种普通分析师“套用”这些模式的局面,就不再需要经历重复的从头的多次迭代才能解决一类问题的尴尬。当然,别忘记我们的总体目标,学会平衡易用性与灵活性,用句行业黑话来说,就是“软件的实现来源于客户的需求,高于需求,最后满足需求。”
PS:所绘图刻意没有加上表示关系的连线,侧重于业务模型的实体对象的识别。
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
在控制台中反复尝试之后,我想到了这种方法,可以按发生日期对类似activerecord的(Mongoid)对象进行分组。我不确定这是完成此任务的最佳方法,但它确实有效。有没有人有更好的建议,或者这是一个很好的方法?#eventsisanarrayofactiverecord-likeobjectsthatincludeatimeattributeevents.map{|event|#converteventsarrayintoanarrayofhasheswiththedayofthemonthandtheevent{:number=>event.time.day,:event=>ev
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何
我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah
鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende
我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss
我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢
好的,所以我的目标是轻松地将一些数据保存到磁盘以备后用。您如何简单地写入然后读取一个对象?所以如果我有一个简单的类classCattr_accessor:a,:bdefinitialize(a,b)@a,@b=a,bendend所以如果我从中非常快地制作一个objobj=C.new("foo","bar")#justgaveitsomerandomvalues然后我可以把它变成一个kindaidstring=obj.to_s#whichreturns""我终于可以将此字符串打印到文件或其他内容中。我的问题是,我该如何再次将这个id变回一个对象?我知道我可以自己挑选信息并制作一个接受该信