10

我正在寻找一些关于如何在每个客户的基础上轻松定制和扩展核心产品的建议。我知道这可能是一个太大的问题。但是,我们确实需要一些想法,就好像我们设置错误一样,它可能会导致我们多年的问题。我在定制和扩展现有产品方面没有太多经验。

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

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

该解决方案作为独立产品运行良好。我们的问题是根据每个客户扩展和定制产品。我们的客户通常希望很快(通常在签订合同后的几天内)获得带有品牌 CSS 和样式的核心产品版本。然而,70% 的客户希望通过定制来改变其运作方式。一些自定义很小,例如域模型、视图模型和视图等的附加属性。其他的更重要,需要全新的域模型和控制器等。

一些定制似乎对所有客户都有用,因此我们希望定期将它们从定制中更改并将它们添加到核心中。

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

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

  • 代码的快速部署——很容易启动一个新客户端以允许品牌/微小的变化
  • 无需复制和粘贴代码
  • 尽可能多地使用 DI 以保持松散耦合
  • 允许为每个客户定制代码
  • 如果我们获得最新版本的核心并重新部署,则能够在一个地方扩展核心产品并让所有客户都获得该功能

非常感谢任何帮助/建议。很高兴添加更多任何人认为会有所帮助的信息。

4

2 回答 2

8

I may not answer to this completly, but here some advices:

  1. Don't copy your code, ever, whatever the reason is.
  2. Don't rename the namespace to identify a given client version. Use the branches and continuous integration for that.
  3. Choose a branching model like the following: a root branch called "Main", then create one branch from Main per major version of your product, then one branch per client. When you develop something, target from the start in which branch you'll develop depending on what you're doing (a client specific feature will go in the client branch, a global version in the version branch or client branch if you want to prototype it at first, etc.)
  4. Try the best to rely on Work Item to track features you develop to know in which branch it's implemented to ease merge across branches.

Targeting the right branch for you dev is the most crucial thing, you don't have to necessary define some hard rules of "what to do in which occasion", but try to be consistant.

I've worked on a big 10 years project with more than 75 versions and what we usually did was:

  • Next major version: create a new branch from Main, dev Inside
  • Next minor version: dev in the current major branch, use Labels to mark each minor versions Inside your branch.
  • Some complex functionnal features was developped in the branch of the client that asked for it, then reversed integrated in the version branch when we succeeded in "unbranded" it.
  • Bug fixes in client branch, then reported in other branches when needed. (you have to use the Work Item for that or you'll get easily lost).

It's my take on that, other may have different point of view, I relied a lot on the Work Item for traceability of the code, which helped a lot for the delivery and reporting of code.

EDIT

Ok, I add some thought/feedback about branches:

In Software Configuration Management (SCM) you have two features to help you for versionning: branches and labels. Each one is not better nor worst than the other, it depends on what you need:

  1. A Label is used to mark a point in time, using a label, for you to later be able to go back to that point if needed.
  2. A Branch is used to "duplicate" your code to be able to work on two versions at the same time.

So using branches only depends on what you want to be able to do. If you have to work one many different versions (say one per client) at the same time: there's no other way to deal with it than using branches.

To limit the number of branches you have to decide what will be a new branch or what will be marked by a label for: Client Specific Versions, Major Version, Minor Version, Service Pack, etc.

Using branches for Client versions looks to be a no brainer. Using one branch for each Major version may be the toughest choice for you to make. If you choose to use only one branch for all major versions, then you won't have the flexibility to work on different major versions at the same time, but your number of branches will be the lowest possible.

Finally, Jemery Thompson has a good point when he says that not all your code should be client dependent, there are some libraries (typically the lowest level ones) that shouldn't be customized per client. What we do usually is using a separated branch tree (which is not per client) for Framework, cross-cutting, low level services libraries. Then reference these projects in the per client version projects.

My advice for you is using Nuget for these libraries and create nuget package for them, as it's the best way to define versionned dependencies. Defining a Nuget package is really easy, as well as setting up a local Nuget server.

于 2012-05-03T11:29:26.003 回答
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")。

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

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

于 2012-05-12T00:41:46.043 回答