什么是良好的解决方案结构,允许基于每个客户轻松定制产品?

Gra*_*ler 10 architecture tfs dependency-injection visual-studio-2010 asp.net-mvc-3

我正在寻找一些关于如何允许基于每个客户端轻松定制和扩展核心产品的建议.我知道这可能是个大问题.然而,我们真的需要得到一些想法,好像我们得到了这个错误的设置,它可能会导致我们多年来的问题.我在定制和扩展现有产品方面没有太多经验.

我们有一个核心产品,我们通常在每个客户的基础上定制.我们最近使用MVC3前端在C#4中重写了该产品.我们已经重构,现在有3个项目组成了解决方案:

  • 核心域项目(命名空间 - projectname.domain.*) - 由域模型(供EF),域名服务接口等(库接口)
  • 域基础设施项目(命名空间-projectname.infrastructure.*) - 实现域名服务EF上下文,仓库实现,文件上传/下载接口实现等.
  • MVC3(namespace - projectname.web.*) - 由控制器,视图模型,CSS,内容,脚本等组成的项目.它还有IOC(Ninject)处理项目的DI.

此解决方案可作为独立产品使用.我们的问题是在每个客户的基础上扩展和定制产品.我们的客户通常希望通过品牌CSS和样式很快(通常在签订合同后的几天内)向他们提供核心产品版本.但是,70%的客户希望通过自定义来改变其运行方式.一些自定义很小,例如域模型,视图模型和视图等的附加属性.其他更重要,需要全新的域模型和控制器等.

某些自定义似乎对所有客户端都有用,因此我们会定期将其更改为自定义并将其添加到核心.

我们目前将源代码存储在TFS中.要启动项目,我们通常会手动将源复制到新的Team Project中.更改命名空间以反映客户端名称并开始自定义基本部件,然后部署到Azure.这显然会导致完全重复的代码库,我确信这不是正确的方法.我认为我们可能应该提供一些能够提供核心功能并在需要时扩展/覆盖的东西.但是我真的不知道该怎么做.

所以我正在寻找有关最佳项目配置的任何建议,以允许:

  • 快速部署代码 - 很容易启动新客户端以允许进行品牌/次要更改
  • 防止复制和粘贴代码
  • 使用尽可能多的DI以保持松散耦合
  • 允许在每个客户端基础上编写代码
  • 能够在一个地方扩展核心产品,并且如果我们获得最新版本的核心并重新部署,所有客户都可以获得该功能

非常感谢任何帮助/建议.很高兴添加任何人认为有用的信息.

Noc*_*ock 8

我可能不会完全回答这个问题,但这里有一些建议:

  1. 无论原因是什么,都不要复制你的代码.
  2. 不要重命名命名空间以标识给定的客户端版本.使用分支和持续集成.
  3. 选择一个如下所示的分支模型:一个名为"Main"的根分支,然后从每个主要产品版本的Main创建一个分支,然后为每个客户端创建一个分支.当您开发某些东西时,根据您正在进行的操作开始定位您将开发的分支(客户端特定功能将在客户端分支中进行,如果您想要原型,则在版本分支或客户端分支中使用全局版本它起初等等)
  4. 尽量依靠工作项来跟踪您开发的功能,以了解它实现的分支,以简化跨分支的合并.

为你开发目标正确的分支是最重要的事情,你没有必要定义一些"在哪个场合做什么"的硬规则,但试着保持一致.

我参与了一个超过75个版本的10年大项目,我们通常做的是:

  • 下一个主要版本:从Main,dev Inside创建一个新分支
  • 下一个次要版本:当前主要分支中的dev,使用标签标记分支内的每个次要版本.
  • 一些复杂的功能特性是在请求它的客户端分支中开发的,然后当我们成功地"无品牌"时,它反转集成在版本分支中.
  • 客户端分支中的错误修复,然后在需要时在其他分支中报告.(你必须使用工作项,否则你很容易丢失).

这是我的看法,其他可能有不同的观点,我在工作项上依赖于代码的可追溯性,这对代码的交付和报告有很大帮助.

编辑

好的,我添加一些关于分支的想法/反馈:

在软件配置管理(SCM)中,您有两个功能可以帮助您进行版本控制:分支和标签.每一个都不比另一个好,也不差,这取决于你需要什么:

  1. 标签用于使用标签标记时间点,以便您以后能够在需要时返回到该点.
  2. 分支用于"复制"您的代码,以便能够同时处理两个版本.

因此,仅使用分支取决于您希望能够做什么.如果你必须同时处理多个不同版本(比如说每个客户端一个版本):没有其他方法可以处理它而不是使用分支.

要限制分支数量,您必须决定新分支的内容或标签将标记的内容:客户端特定版本,主要版本,次要版本,Service Pack等.

使用客户端版本的分支看起来并不是一件容易的事.为每个主要版本使用一个分支可能是您做出的最艰难的选择.如果您选择仅对所有主要版本使用一个分支,那么您将无法灵活地同时处理不同的主要版本,但您的分支数量将尽可能低.

最后,Jemery Thompson有一个很好的观点,他说并非所有代码都应该依赖于客户端,有些库(通常是最低级别的代码库)不应该按客户端进行自定义.我们通常使用的是分离的分支树(不是每个客户端)用于框架,交叉,低级服务库.然后在每个客户端版本项目中引用这些项目.

我的建议是将Nuget用于这些库并为它们创建nuget包,因为这是定义版本化依赖项的最佳方法.定义Nuget包非常简单,并且设置本地Nuget服务器.

  • 分支机构在TFS中的成本并不高,因此您唯一担心的是如何有效地管理数量. (2认同)

Jer*_*son 4

I just worried that with 30 or 40 versions (most of which aren't that different) branching was adding complexity.


+1 好问题,这更多的是您必须做出的商业决策:

我是否想要一个整洁的代码库,可以轻松维护,并且可以快速向所有客户推出功能和修复

或者我是否想要将一个代码库的大量实例分开,每个实例都进行一些微小的调整,这是很难的(编辑:除非你是一个可以“取消品牌”的东西的 ALM MVP)合并到一个主干中。


我同意 @Nockawa 提到的几乎所有内容,除了恕我直言,不要用分支来替代扩展代码架构。

绝对使用分支/主干策略,但正如您所提到的,太多分支使得quickly推出站点范围的功能变得更加困难,并阻碍项目范围的持续集成。如果您希望防止复制/粘贴,请限制分支数量。

就编码解决方案而言,我相信您正在寻找以下内容:

  • 模块/插件、接口和 DI 正是目标!
  • 从基础类派生自定义类(扩展每个客户的 DSL,Assembly.Load())
  • 自定义报告解决方案(许多自定义请求可以是报告,而不是新页面)
  • 带有电子表格的页面(呵呵我知道 - 但有趣的是它有效!)

模块/插件点的一个很好的例子是 CMS,例如DotNetNuke 或 Kentico。其他想法可以通过查看 Facebook 的插件架构、用于音频和视频编辑的插件、3D 建模应用程序(如 3DMax)以及可让您构建自己的关卡的游戏来获得。

理想的解决方案是一个管理应用程序,您可以选择模块 (DLL)、定制 CSS(皮肤)、编写 DB 脚本并将解决方案自动部署到 Azure。为了实现这个目标,插件会更有意义,代码库不会被分割。此外,当对模块进行增强时,您可以将其推广给所有客户。

您可以轻松地进行小型定制,例如使用用户控件、派生类和函数覆盖在域模型、视图模型和视图等上添加附加属性。

真正做到这一点,假设客户说我想要一个标签来统计系统中每个人的年龄,创建一个名为的函数int SumOfField(string dBFieldName, string whereClause),然后为该客户站点提供一个绑定到该函数的标签。然后假设另一个客户想要一个函数来统计客户购买的产品数量,您可以重复使用它:SumOfField("product.itemCount","CustomerID=1")。

需要全新的域模型和控制器等的更重大的变化将适合插件架构。一个例子可能是客户需要第二个地址字段,您可以将当前的地址用户控件调整为任何页面的插件,它将具有设置以了解可以实现其 CRUD 操作接口的 dB 表和字段。

如果功能是在 30-40 个分支中为每个客户端定制的,那么可维护性将变得非常困难,因为我感觉您将无法(轻松)将它们合并在一起。如果有可能这会变得非常大,您不会想管理 275 个分支机构。然而,如果它是专业的,你必须深入到每个客户端的用户控制级别,并且“用户无法设计自己的页面”,那么使用 Nockawa 的前端分支策略是完全合理的。