所以,我需要一些输入重构一个asp.net(c#)应用程序,它基本上是一个用于创建动态表单(任何表单)的框架。从高层的角度来看,有一个表格有表格,然后有一个表格,其中包含所有表格字段,两者之间是一对多。有一个验证表,其中每个字段可以有多种类型的验证,从表单字段表到验证表是一对多的。
所以问题是这个应用程序已经作为所有客户端的所有可定制解决方案出售。所以,这个想法是他们想要的任何形式,我们可以使用数据库配置来构建它。问题是,这并不总是可行的,因为字段之间存在复杂的关系,而且表单本身之间存在复杂的关系。此外,只有一次代码库,这适用于多个客户端 - 所有客户端都是自己托管的。每个客户端都有非常特定的逻辑,它们在同一个代码库中是ALL,没有真正的分离。有时候使它变得通用太难了,所以有些情况下它有硬编码逻辑(如果formID = XXX则执行 _ )。您也可以在每个表单中拥有嵌套表单,例如,一组字段。
通常,当一个客户端请求更改时,我们进行更改并将其部署到该客户端 - 但是然后另一个客户端请求不同的更改,我们进行更改并为该客户端部署它,但是从早期的客户端打破它,并且很难尝试调试,因为一切都是动态的。我们无法回滚先前的更改,因为那时其他客户端将被搞砸。
它不是在真正的3层架构中完成的 - 它是一个引用了DB类的网站,以及一个类库。网站本身,类库中存在业务逻辑,数据库存储过程(验证在存储过程中完成)。
我一直负责重组整个事情,这些是我的想法/问题:
我认为这通常是一个糟糕的模型,因为我听到其中一位开发人员说过的一件事是,任何时候任何客户都做出改变,我们应该部署给每个人 - 但这是不现实的,如果我们已经说了20个客户 - 需要对一切进行回归测试,因为我们不知道影响...
总共有大约100种形式,它们之间有一些相似之处(不多)。但我认为动态引擎可以解决所有表单请求的想法也不现实。客户提出了最奇怪的要求。例如,他们使用此引擎执行常规数据输入表单和搜索表单。
页面之间有很多保留状态,并且都是使用会话变量完成的,这是好的,除了它没有被真正跟踪,因此来自同一用户的会话不断被覆盖,我认为会议应该摆脱。
我真的应该重写整件事吗?这个应用程序大约3岁,已经进行了大量的测试和事情,并且实施了严格的业务逻辑,所以我讨厌摆脱所有这些(joel的建议)。但它真的是一堆sphagetti代码,一切都需要永远做,而且由于微小的变化,事情一直在破碎。
我一直在阅读Martin Fowlers“Refactoring”和Michael Feathers“有效地使用遗留代码” - 而且它们很好,但我觉得它们是针对一个'稍微'更好的架构的应用程序编写的,它仍然是一个三层架构,并且存在“某种”逻辑相似性。
思考/输入任何人?
哦,还有“帮助!”
答案 0 :(得分:4)
我有很多类似的应用程序用于构建我支持的动态表单。
你可以/不可以做很多事情&在放弃3年的测试/开发之前,你是正确的思考。
我要求您考虑的是在您所拥有的基础上实现插件架构。任何表单的自定义代码都插入到插件中。此插件的名称与表单一起存储。生成表单时,将调用正确的插件以增强基本功能。这样你就可以将所有自定义代码移出现有的库。它也应该意味着更少的更改,每个插件只影响它附加的形式。
从那时起,重构核心引擎将很容易,因为它是所有客户端的通用功能。形式。
答案 1 :(得分:4)
我当前的项目听起来几乎与您描述的产品完全相同。幸运的是,我在以前的产品上学到了大部分最难的课程,因此我能够以干净的方式开始我当前的项目。您应该阅读my answer to this question,其中描述了我的经历以及我学到的经验教训。
要关注的主要问题是您正在构建产品。如果您无法使用当前的产品功能集找到实现特定功能的方法,则需要花费一些额外的时间来考虑如何将此自定义一次性功能转换为可使所有功能受益的可配置功能(或者至少很多客户。
所以:
答案 2 :(得分:0)
由于您的应用程序似乎已成为一个很大的漏洞,完整(或几乎完全重写)可能是有道理的。
您还应该考虑面向文档的数据库(couchDB,MongoDB)等新技术
大多数表单定义可能非常适合面向文档的数据库。例如:
要定义客户表单,您可以使用如下文档:
{Type:"FormDefinition",
EntityType: "Customer",
Fields: [
{FieldName:"CustomerName",
FieldType:"String",
Validations:[
{ValidationType:"Required"},
{ValidationType:"StringLength", Minimum:15, Maximum:50},
]},
...
{FieldName:"CustomerType",
FieldType:"Dropdown",
PossibleValues: ["Standard", "Valued", "Gold"],
DefaultValue: ["Standard"]
Validations:[
{ValidationType:"Required"},
{
ValidationType:"Custom",
ValidationClass:"MySystem.CustomerName.CustomValidations.CustomerStatus"
}
]},
...
]
};
使用这种文档来定义表单,您可以轻松添加客户特定的表单和验证。
您可以使用SubForm或其他类型的字段类型轻松添加子表单。
您可以为所有常见类型的字段定义字段类型,例如电子邮件,电话号码,地址等。
namespace System.CustomerName.CustomValidations {
class CustomerStatus: IValidator {
private FormContext form;
private List<ValidationErrors> validationErrors;
CustomerStatus(FormContext fc) {
this.validationErrors = new List<ValidationErrors>();
this.form = fc;
}
public List<ValidationErrors> Validate() {
if (this.formContext.Fields["CustomerType"] == "Gold" && Int.Parse(this.form.Fields["OrderCount"]) < 10) {
this.validationErrors.Add(new ValidationError("A gold customer must have at least 10 orders"))
}
if (this.formContext.Fields["CustomerType"] == "Valued" && Int.Parse(this.form.Fields["OrderCount"]) < 5) {
this.validationErrors.Add(new ValidationError("A valued customer must have at least 5 orders"))
}
return this.validationErrors;
}
}
}
具有该定义的文档记录可能如下所示:
{Type:"Record",
EntityType: "Customer",
Fields: [
{FieldName:"CustomerName", Value:"ABC Corp.",
{FieldName:"CustomerType", Value:"Gold",
...
]
};
当然,这个解决方案需要做很多工作,但是如果/什么时候实现的话,创建/更新/自定义表单真的很容易。
答案 3 :(得分:0)
这是一种常见但(IMO)有点天真的设计方法。 “而不是解决客户的问题,让我们建立一个工具,让他们解决自己的问题!”。但实际情况是,客户通常希望您解决他们的实际问题。因此,建立解决问题的方法。 如果您能够以允许您为不同客户重用某些部件的方式构建它,那很好。但这通常是框架已经为您完成的工作 - 找出应用程序所需的常用功能,并使它们以整洁的包装形式提供。