.NET Framework

自适应访问层 + 依赖关系注入 = 效率

Ulrik Born

最艰巨的挑战时管理和开发一个复杂的企业解决方案的源代码,是确保基本代码之一仍然一致、 直观的和高度可测试正在同时保持和扩大了在大型软件开发署内的多个小组。核心问题是开发人员通常有大量的规则和准则,遵循和一组实用程序库,若要使用,往往会导致他们的解决方案变得更大、 更复杂。这是因为他们最终调整理想、 直观的业务逻辑实现,使其遵守规则和适合固定的 Api。这更通常意味着更多的工作,对于开发人员来说,bug、 较少的标准化、 少重用,并降低整体生产力和质量。

我是为领先的在线投资银行高级开发人员,已观察到这些挑战如何可以限制生产力。这篇文章是一个案例研究,提出了我们的开发团队已经如何分析和克服的挑战的创新使用的运行时代码生成和依赖关系注入 (DI)。你可能不同意的一些我们的团队设计的选择,但我相信你会同意他们代表解决一些常见的建筑挑战的新鲜和有效的方式。

我公司拥有大内部软件开发部 (工作在两个大陆上的),不断维护和扩展了我们巨大的 Microsoft.NET 框架代码库。我们的代码库重点是众多任务关键型 Windows 服务,弥补在我们数据中心主办的高性能、 低延迟的交易系统。我们有不少平台团队守护的代码库和运行时环境,再加上许多项目团队,不断 (和并行) 改善和扩展系统。

我上平台团队工作几年,经验丰富的缺点的不一致和过于复杂代码库期间多次审查和支持情况。两年前,我们决定来解决这些问题,并发现了以下问题:

  • 我们有了太多的相同的基本问题解决方案。一个好的例子是我们大多数的 Windows 服务有自己独特的方式与相结合的各种 Api 成一个简单的服务适当支持日志记录、 跟踪、 数据库访问等等。
  • 我们的业务逻辑实现了要么简单 — —­但不是单位可测试和太幼稚、 不遵守准则 — — 或由于大量水暖代码过于复杂。一个常见的例子:简单的代码工作直接在.NET SQL 服务器 API 与琐细的管道,支持自动重试次数,花更多线条的复杂代码,所以比上的高速缓存的实际业务逻辑。
  • 我们有实用程序库支持大多数我们的建筑原则和编码准则,但是他们在几个不同的样式中实现并独立地进化。结束时即使使用它们作为听写的指导方针时,每个功能解决方案已有巨大足迹的引用的程序集和往 API 更改曝光。这反过来让这一复杂的任务本身投入在生产中的一项新功能,也使它难以更新实用程序库。
  • 整体的一套准则和要应用的规则和实用程序,以使用只是那么大只有我们大多数经验丰富的开发人员已经为新的开发人员理解一切,和进入壁垒的一个公平的机会是极高。这意味着大量的非标准代码书面的要么以后丢弃或到达生产以增加不一致。
  • 我们的核心 Windows 服务的几个了中央"注册点"所有项目团队都必须触摸相同的代码的地方 — — 例如,大型 switch 语句调度命令或工作岗位。这使得非平凡代码合并到我们主要分支。

Natrually,这些问题是既不是新的也不是唯一给我们,和一些既定的设计模式描述如何解决此类问题:

  • 门面模式隐藏访问一个简单的访问层界面背后是复杂的资源的所有细节。这便于清洁和可测试业务逻辑实现在外部资源可以被轻松地嘲笑出来在测试中。
  • DI — — 或控制反转 (IoC) 容器 — — 允许组件的松散耦合的因此易于扩展、 维护和结合起来。这种技术也容易地模拟出选定的组件,从而提高可测试性。
  • 精心设计实用程序库的 Api,不要强制消费代码予以调整 ; 相反,他们支持直观执行。

我们已经知道这些模式多年,也应用他们都以各种形式在整个我们的代码库。但是几个基本问题大大限制了这些模式的成功。第一,门面模式并不消除对很多管道代码的需要 — — 它只是将其移动到另一个类,通常只是手段更工作为开发人员。第二,除非 DI 容器会自动发现它的组件在运行时 (例如,通过属性),它仍将需要一个中央注册,并将只是现实中引入附加层执行。而且,最后,它是成本高昂而且极难设计和实施在同一时间是直观的、 灵活的和有用的 Api。

为什么我们创建了自适应访问图层

集思广议会议次数后, 我们来到了一个单一的灵活且功能强大的解决方案与所有的这些问题。基本思想是以隐藏所有 Api 都归因于的访问层接口背后并生成一个执行引擎,可以在运行时中遵循所有的规则和准则的方式实现这种接口。我们要求这项技术自适应访问层 (AAL),因为每个解决方案定义的访问层接口,它需要高度灵活的方式。我们结合开源,属性驱动 Autofac DI 容器的 AAL 执行引擎,实现灵活的 Windows 服务框架,使清洁、 直观和可测试实现成为最简单的选项。图 1 阐释了如何的大小、 足迹和解决方案的复杂性大大减少当 AAL 用于解耦核心逻辑实现从周围的库和 Api。蓝框代表一个单一的解决方案,(在左侧) 与 AAL 执行的足迹和无 (右侧)。


图 1 自适应访问层 (描述上左) 大大降低解决方案的复杂程度和足迹

AAL 技术的一个关键特征是它给我们一个共同的中心位置,不会污染的业务逻辑代码执行我们的最佳做法和准则。在这方面,AAL 是类似于面向方面编程 (AOP)、 各种拦截功能和代理技术。主要的区别是 AAL 隐藏底层的 Api 来从消费的业务逻辑代码,而其他技术仍然公开他们,从而大大增加解决方案足迹。

为了说明这个想法,我将讨论一些业务逻辑和标准的 Windows 事件日志之间的简单访问层。考虑在数据库中注册传入订单订购登记服务。如果数据库调用失败,则该服务必须写入事件日志错误。

在经典的方法中,这可能涉及对.NET EventLog.WriteEntry 方法的调用,可能看起来像中的代码图 2。这种方法不是最优,原因有二。第一,它不是非常适合的单元测试,该测试会检查运行单元测试来验证与正确的文本条目实际写入计算机上的事件日志。和第二、 四行的琐细的水管系统的代码会"污染"的业务逻辑的一个核心部分。

图 2 经典事件日志访问

 

public OrderConfirmation RegisterOrder(Order order)
{ 
  try 
  {  
    // Call database to register order and return confirmation 
  } 
  catch (Exception ex) 
  {   
    string msg = string.Format("Order {0} not registered due to error: {1}",     
      order.OrderId,     
      ex.Message);   
    _eventLog.WriteEntry(msg, 1000, EventLogEntryType.Error); 
  }
}

这两个问题被针对引入 AAL 的业务逻辑和底层的 EventLog 类之间的接口。 这种一层下面的代码所示:

[EventLogContract("OrderService")]
public interface IOrderServiceEventLog{
    [EventEntryContract(1000, EventLogEntryType.Error,
        "Order {0} not reg due to error: {1}"]
    void OrderRegistrationFailed(int orderId, string message);
}

图层是由化接口 IOrderServiceEventLog,通过实施动态类由执行引擎在运行时定义的。 接口本身具有 [EventLogContract] 的属性以允许执行引擎承认它是一个事件日志的访问层。 单个参数是为目标的事件日志的名称。 有对名称的接口或方法上它的数目没有限制。 每个方法必须返回的 void (有向事件日志写入信息时是没有意义的返回值),并且有 [EventEntryContract] 属性。 该属性将所有固定元数据输入 (id、 严重性和格式) 作为参数,这不再需要在业务逻辑中。

接口使用访问层、 业务逻辑从图 2 成为了很多更小和更清晰:

public OrderConfirmation RegisterOrder(Order order)
{
    try  
    {
        // Call database to register order and return confirmation
    }
    catch (Exception ex)  
    {
        _logLayer.OrderRegistrationFailed(order.Id, ex.Message);   
    }
}

示例 RegisterOrder 方法现在是简单、 高度可读和可更多测试,因为验证不再要求检查事件日志而是只小模拟实现访问层接口的类。 另一个优点是可以通过整个订单服务映射事件日志已满的互动和从而提供一个简单的然而什么事件日志条目的完整概述系统写入 IOrderServiceEventLog 接口。

(注意:简短的一边,作为最近语义日志记录应用程序块 [板] 对新.NET 4.5 EventSource 类拥抱将元数据从代码移动到属性和公开自定义的、 强类型的测井方法,而不是通用的几种方法同样的想法。 若要使用板,开发人员必须实现从 EventSource 类派生的自定义类和使用此类整个代码库。 我相信我们的方法是作为板一样强大但易于使用,因为它只需要开发人员定义的接口和类的实现不。 EventSource 类的一个关键特征是它支持通过接收器可配置集的结构化的事件日志记录。 我们的访问层实现当前不支持结构化的日志记录但可以轻松地扩展为这样做,因为它有访问通过访问层方法的参数的结构化信息。)

我还没尚未考虑 RegisterOrder 方法的真正主体,即对一些 SQL Server 数据库的调用存储过程来坚持的顺序进行进一步处理。 如果我的团队使用.NET SqlClient API 来实现这个,它将至少 10 行的琐细的代码来创建 SqlConnection 实例和 SqlCommand 实例,填充带从顺序属性参数的命令、 执行命令和读回的结果集。 如果我们要满足额外的要求,如在数据库死锁或超时的情况下自动重试,我们可以轻松地结束 15 到 20 行代码只是为了让一个相当简单的调用。 所有这一切需要和只是因为调用的目标发生了是一个存储的过程,而不是进程在.NET 方法。 从业务逻辑的观点看,是绝对没有理由为什么我们核心执行应该如此混乱和复杂,只是因为加工十字架从一个系统到另一个。

通过引入类似的事件日志访问层自适应数据库访问层,我们可以实现简单、 可测试身体:

public OrderConfirmation RegisterOrder(Order order)
{
  try
  {
    return _ordersDbLayer.RegisterOrder(order);
  }
  catch (Exception ex)
  {
    _logLayer.OrderRegistrationFailed(order.Id, ex.Message);
   }
}

到目前为止,我已经说明了想法、 灵活性和 AAL 的力量。 我们已经开发了,发现有用的访问层更详细攻略,我们现在就随。 我会从开始上述数据库访问层。

数据库访问层数据库访问是大多数企业系统中,包括我们的核心部分。 我们正在关键的调解人的严重在线交易的金融工具,必须满足一些严格的性能和安全要求由我们的客户和金融当局,要求,因此被迫仔细地保障我们的数据库。 我们一般通过这样做只做数据库访问通过存储过程中,如让我们应用细粒度安全规则和审查所有数据库的查询性能和服务器负载之前他们打我们的生产系统。

我们已经仔细评估是否存储过程的对象关系映射 (ORM) 工具如实体框架可以帮助我们实现更简单和更可测试代码,而无需移动远离。 我们的结论是实体框架是一个极具吸引力的解决方案,但它很大程度依赖于能够编写和执行复杂的 SQL 语句在运行时。 它可以将映射的存储的过程,但限于映射唯一的存储的过程,它失去了大部分的它的好处。 为此原因,我们决定实施我们自己的数据库访问框架作为自适应数据库访问层。

我们的执行支持存储的过程调用,选择意见和有效的大规模插入通过 SQL Server 批量复制功能了。 它可以映射到存储的过程参数输入的数据直接从数据传输对象 (DTO) 类的属性和同样可以将结果集的列映射到类的属性。 做.NET 代码中的数据库访问时,这有助于明确和直接的语法。

下面的代码演示一个简单的层,适合的示例命令注册服务:

[DatabaseContract("Orders")]
public interface IOrdersDatabase{
  [StoredProcedureContract("dbo.RegisterOrder",
    Returns=ReturnOption.SingleRow)]
   OrderConfirmation RegisterOrder(Order order);
 }

此代码映射单个存储的过程和结果单排在结果集中到 OrderConfirmation 实例初始化从结果集列。 从给定的命令实例的属性设置映射的存储过程的参数。 此映射行为定义在 [StoredProcedure­合同] 属性,因而不再需要在业务逻辑执行,使这一清晰和可读。

因为我们认为它是简单而有效的方式向我们的开发人员提供标准的功能,而不限制他们的自由,以最自然和最直观的方式实现他们的业务逻辑的我们已经在数据库访问层实施一些相当高级的功能。

支持的功能之一是对大容量插入行通过 SQL 批量复制功能的无缝支持。 我们的支持使我们开发人员可以定义一个简单的方法,采用的表示要作为输入插入的行的 DTO 类的可枚举集合。 访问层处理所有的细节,从而减轻 15 到 20 的代码的行复杂,以数据库为中心的业务逻辑。 这种大容量复制支持是一个完美的例子,概念上很简单操作的 — — 以行插入到表中的有效方式 — — 通常最终被相当复杂,简单地实施因为基础.NET 框架 SqlBulkCopy 类发生工作 IDataReader,而不是直接在我们 DTO 类上。

数据库访问层是我们实施的第一个,它已从一开始一个巨大的成功。 我们的经验是我们编写更少、 更简单行代码与它和我们的解决方案自然成为高度单位可测试。 基于这些积极的成果,我们很快意识到我们可以受益于我们的业务逻辑代码和其他几个外部资源之间引入 AAL。

服务访问层我们交易系统的实施是高度面向服务、 和鲁棒间服务通信对于我们至关重要。 我们的标准协议是 Windows 通信基础 (WCF),我们有很多的重点是使 WCF 调用的代码。

大部分的这些实现按照相同的总体格局。 第一,终结点的地址是解决 (我们通常运行我们的服务或者在主动-主动或主动-被动设置)。 然后 ChannelFactory.NET 类用于创建对其所需的方法调用一个通道类实现。 如果此方法成功,通道是关闭和释放,但如果它失败,异常也要进行检查。 在某些情况下,重试的方法上同一个终结点,而在其他情况下它是更好地做自动故障转移和重试的其他可用的终结点之一是有意义的。 在此之上,我们常常想要隔离很短的时间,以不重载它与连接尝试和失败的方法调用失败终结点。

它很不琐碎,写这种模式,正确执行,它可以很容易地 10 到 15 行代码。 并再次,这种复杂性介绍了只是因为我们需要调用的业务逻辑发生承载在另一项服务,并不在进程。 我们已经实现了自适应服务访问层,消除这种复杂性,使之尽可能简单和安全地调用远程方法,因为它是调用进程的方法。

与相同的数据库访问层的原则和工作流。 开发人员将写入化的接口映射只有她需要调用的方法和我们执行引擎创建的最佳实践行为作为指定在属性中的使用实现接口的运行时类型。

下面的代码显示映射单个方法小服务访问层:

[ServiceAccessLayer(typeof(IStatisticsSvc), 
  "net.tcp", "STATISTICS_SVC"]
public interface ISalesStatistics{
  [ServiceOperationContract("GetTopSellingItems")]
  Product[] GetTopProducts(int productCategory);
}

接口属性标识基础纯 [ServiceContract] 归因于接口 (用于对 ChannelFactory 的内部调用)、 要使用的协议和服务调用的 id。 后者用作键进入我们的服务定位器,在调用时解决实际的端点地址。 访问层将在默认情况下使用默认 WCF 绑定为给定的协议,但这可以通过在 [ServiceAccessLayer] 属性上设置的附加属性自定义。

ServiceOperationContract 的单个参数是操作的动词,用于标识基本的 WCF 服务合同中的映射的方法。 其他的可选参数的属性指定是否应缓存服务调用结果和是否始终是安全的自动故障转移操作即使 WCF 终结点的第一个调用失败后目标服务上已执行的代码。

其他访问层我们也建立了类似 AAL 用于跟踪文件、 性能计数器和我们消息总线。 他们都基于前面的示例中所示的同一原则 — — 即,要允许的业务逻辑来表达其资源访问的可能的最简单的方式由移动到的所有元数据属性。

依赖关系注入一体化

与图层的访问,我们的开发人员不再需要执行大量的琐碎水暖代码,但仍必须有一种方法来调用,AAL 执行引擎,以获得执行的运行时类型的实例,每当该映射的外部资源已被称为。 执行引擎可以直接调用,但那样会违背我们的保持清洁和可测试业务逻辑的原则。

我们已经通过注册我们的执行引擎作为一个动态注册来源与 Autofac 这样每当 Autofac 不能解决与静态注册的任何依赖项时,就会调用来处理这个问题。 在这种情况下,Autofac 会问,执行引擎是否它可以解决给定的类型和 id 的组合。 引擎将检查类型,并提供该类型的实例,如果该类型是归因于的访问层接口。

这个地方,我们已经建立了在业务逻辑实现简单地可以声明其访问层接口的类型和环境把他们当作参数 (例如,在类的构造函数),然后 DI 容器将能够解决这些参数通过援引在幕后执行引擎的信任。 此类实现自然会在接口上的工作,并且很容易测试,它只需几个 mock 类来实现这些接口。

实施

所有我们访问层是使用相同的技术来实现的。 总的想法是在普通的 C# 代码中的抽象基类中实现的所有功能,然后仅使用发出以生成薄类从基类派生并实现该接口。 发出的方法体的每个接口简单地将转发到一般的 Execute 方法在基类中执行。

这一般方法的签名是:

object Execute(Attribute, MethodInfo, object[], TAttributeData)

第一个参数是从中调用 Execute 方法的访问层接口方法的方法属性。 它通常包含的所有元数据 (例如,存储的过程的名称、 重试规范等等) 所需的 Execute 方法,以提供正确的运行时行为。

第二个参数是接口方法的反射的 MethodInfo 实例。 它包含有关该实现的方法的完整信息 — — 包括的类型和方法参数的名称 — — 和 Execute 方法用于解释第三个参数。 它包含所有参数的值到当前接口方法调用。 Execute 方法通常转发这些值到基础资源的 API,例如,作为存储的过程的参数。

第四个参数是自定义类型,拥有固定的数据,用于在每次调用该方法,使其尽可能高效。 固定的数据被初始化一次 (由抽象基类中的方法) 时,引擎实现的运行时类。 我们数据库访问层使用此功能来检查只有一次的存储的过程和准备准备好使用的 SqlCommand 模板时调用该方法。

传递到 Execute 方法的特性和 MethodInfo 参数也只有一次反映并重复使用在每个方法调用,又尽量减少每个调用的系统开销。

执行的返回值用作实现的接口方法的返回值。

这种结构相当简单,已变得灵活和强大。 我们已经在通过一个抽象的、 共同的基类 AccessLayerBase 该类的所有我们访问层中重用它。 它实现了所有所需的逻辑化的接口和驱动器发出一个新的运行时类的过程进行检查。 每个接入层类别都有自己专门的抽象基类从 AccessLayerBase 派生。 它拥有的实际执行情况访问外部资源,例如,使存储的过程调用根据我们所有的最佳实践。 图 3 显示为示例数据库访问层界面执行类层次结构。 蓝色部分是 AAL 的框架 ; 红色部分是归因于的接口定义的业务逻辑功能解决方案 ; 绿色部分中,排放的 AAL 执行引擎的运行时类。

An Access-Layer Implementation Outline
图 3 访问层实施纲要

图 3 也说明了我们如何让基类实现一组公共接口 (从 IAccessLayer 派生) 以公开关键行为信息。 这不打算用于由业务逻辑实现,但而是通过基础设施的逻辑 — — 例如,要跟踪每当一个存储的过程调用将失败。

这些访问层接口也是在业务或技术要求要求对后面的访问层基础资源的访问不完全支持 AAL 的方式做的几个特殊情况下很有用的。 有了这些接口,我们的开发人员可以使用 AAL 但拦截和调整底层操作以满足特殊的要求。 一个很好的例子是 IDatabaseAccessLayer.ExecutingCommand 事件。 这引发右前 SqlCommand 执行的使我们能够通过改变事物如超时值或参数来自定义它。

报告和检查

归因于 AAL 接口的一个业务逻辑解决方案,还使我们能够反映在已编译的二进制文件生成时间和提取数量的有用的报告。 我们的团队已纳入这在我们团队的基础服务器 (TFS) 生成这样每个生成的输出现在包括几个内容丰富的小 XML 文件。

报告生成时间数据库访问层报告完整列表的所有存储的过程、 视图和大容量插入正在访问。 我们使用此来简化点评并检查所有所需的数据库对象正确部署和配置之前我们释放的业务逻辑。

同样,我们的事件日志的访问层报告一项服务可以生成的事件日志条目的完整的列表。 我们的生成后步骤采取此信息,并把它变成我们 Microsoft 系统中心操作管理器生产环境监测的管理包。 这是聪明的因为它确保运营经理始终是最新的正确的信息,关于如何最佳处理生产问题。

自动化 Microsoft 安装程序程序包我们已经应用相同的反射技术,收获宝贵的投入到我们为我们的 Windows 服务生成作为我们 TFS 的最后一步生成的 Microsoft 安装程序 (MSI) 程序包。 这些套件的一个关键点是要安装和配置事件日志和性能计数器以确保它们匹配正在部署的业务逻辑。 生成从二进制文件中提取事件日志名称和性能计数器定义和自动生成 MSI 软件包,安装这些名称和定义。

运行时检查使用报告从我们生产环境的最常见错误之一是一项服务曾试图调用的存储的过程,并不存在或存在了与生产数据库上的错误签名。 这些类型的错误发生了因为我们错过了部署的所有所需的数据库对象,当我们生产部署 Windows 服务。 这里的关键问题并不缺少部署本身,因为那可以修复很容易,但更多的事实该错误不是发生在部署但通常后来当存储的过程是第一次调用期间营业时间。 我们使用了基于反射的所有访问的数据库对象列表来解决这一问题,让我们的 Windows 服务在服务启动过程中验证的存在性和有效性的所有对象。 该服务只需运行通过对象的列表,然后查询数据库,为每一个要检查它将能够访问该对象时所需。 这种方式,我们已经所有此类错误从营业时间到部署时间,当它是很多更安全和更容易地解决这些问题。

下面列出了这些附加的用法来说明 AAL 的一个主要好处。 与有关服务行为很容易可通过反射几乎完成的信息,一个全新的智能化报告、 建筑、 自动化和监测开辟了。 我们的团队收获了到目前为止,这些好处的一小部分,但我们看到更多有趣的应用未来数。

生产力和质量

AAL、 设计和实施在过去两年,已证明是非常强大的调解人的提高开发人员生产率和增加的解决方案质量为我们公司的开发团队。 我们已经降低我们的成本,准备从周小时到新的 Windows 服务和现有的服务从天延长到几分钟。 这已改进我们的灵活性,从而做为我们发展我们的客户产品更便宜。

实现绝大多数的我们的业务解决方案时,我们访问层是适当的。 但是,我们有几个特殊情况下他们不适合 — — 通常是在高度可配置的方案,例如,当从配置表中读取并在编译时不知道要调用的存储过程的名称。 我们的团队有故意选择不支持这种情况下,为了避免将引入附加的框架复杂性。 相反,我们允许我们在这种孤立的情况下使用普通的.NET Api 的开发人员。

AAL 解决办法本身并不大,在几个月内会在两年期间已经制定。 因此,我们的初始投资不是很高,已达到盈亏平衡状态,通过大量的已保存的发展和支持小时。

当然,有一个广泛应用和高度灵活的平台的挑战是它可以成为一个单点故障。 我们减轻了这个由拥有完整的单元测试覆盖率的 AAL 解决方案和推出它的新版本中受管辖的服务的服务方式。 你也可以认为 AAL 办法本身我们的系统中引入额外的复杂性,并迫使我们开发人员学习新的抽象层。 但我们认为这超过补偿提高整体生产力和质量。

我们知道的另一个问题是我们必须把重点放在整体设计和体系结构并不只是简单地把 Windows 服务的一切都因为这种做法已变得如此便宜和容易。 有时候 IIS 承载 Windows 进程激活服务 (WAS) 或第三方解决方案提供一个更好的总体解决方案,即使它将添加到我们的生产环境的多样性。

Ulrik Born 信息技术从丹麦技术大学硕士学位和一直以来在过去的 15 年作为开发商和建筑师在 Windows 平台上。他是领先开发者,盛宝银行、 基于互联网的投资银行。

衷心感谢以下技术专家参与本文的审阅:古德永松乔纳斯 (盛宝银行)、 詹姆斯 · 麦卡弗里 (Microsoft) 格里梅尔 (Microsoft) 和费尔南多 · Simonazzi (Microsoft)
乔纳斯 · 古德永松、 副总裁和企业的建筑师,是首席技术官办公室负责盛宝银行,包括战略、 原则和准则的四个建筑领域的总体 IT 体系结构的一部分

博士。 詹姆斯 · 麦卡弗里为微软在华盛顿州雷德蒙德,校园的工作。 他已为多种 Microsoft 产品效过力,包括 Internet Explorer 和 MSN Search。 他是《.NET Test Automation Recipes》(Apress,2006)的作者,你可以通过以下电子邮箱地址与他联系:jammc@microsoft.com

博士。 格里戈里 Melnik 是微软模式的首席项目经理 & 实践团队。 这些天他开车的微软企业库、 统一、 CQRS 的旅程和 NUI 模式项目。 他还设计为它促进效率。 在此之前,他是一个足够长的时间前的研究员和软件工程师还记得在 Fortran 编程的乐趣。 格里戈里讲话世界各地关于主题的代码重用、 云计算、 敏捷方法和软件测试。 他还担任 IEEE 软件咨询委员会。 在他博客 https://blogs.msdn.com/agile

Fernando Simonazzi 是一个软件开发商和建筑师有超过 15 年的专业经验。 他已经贡献者微软模式 & 包括企业图书馆、 统一,CQRS 的旅程和棱镜的几个版本的做法的项目。 费尔南多是副研究员 Clarius 咨询。