这个问题与语言无关,但我是C#家伙所以我使用术语POCO来表示一个只能预先形成数据存储的对象,通常使用getter和setter字段.
我只是将我的领域模型重新设计成超级PCOO,并且对于如何确保属性值在域中有意义而留下了一些问题.
例如,服务的EndDate不应超过服务所在合同的EndDate.但是,将检查放入Service.EndDate设置器似乎违反了SOLID,更不用说随着需要完成的验证数量增加,我的POCO类将变得混乱.
我有一些解决方案(将在答案中发布),但它们有其缺点,我想知道解决这一难题的最佳方法是什么?
language-agnostic separation-of-concerns solid-principles modular-design
在计算机科学中,我们被教导每种方法应该只做一件事,一件事.我有点困惑,然后我们看到MVC行为如下所示作为良好实践的例子:
[AcceptVerbs(HttpVerbs.Post), Authorize]
public ActionResult Edit(int id, FormCollection collection) {
Dinner dinner = dinnerRepository.GetDinner(id);
if (!dinner.IsHostedBy(User.Identity.Name))
return View("InvalidOwner");
try {
UpdateModel(dinner);
dinnerRepository.Save();
return RedirectToAction("Details", new { id=dinner.DinnerID });
}
catch {
ModelState.AddModelErrors(dinner.GetRuleViolations());
return View(new DinnerFormViewModel(dinner));
}
}
Run Code Online (Sandbox Code Playgroud)
基本上这段代码提供了很多功能:
对我来说,这似乎对一种方法的责任太多了.它也是一个相当简单的动作,即它不处理常见的情况,如:
不要太提及围绕这种方法所需的测试量,即FormCollection/UserIdentity/Authorization Provider/Repository/etc的模拟/伪造.
我的问题是我们如何避免在控制器操作中塞入如此多的东西?
我倾向于认为"意见"是一个伟大的概念,尤其是"Thunderdome Principle".虽然我非常尊重参与构建FubuMVC的人 …
我有一个以数据为中心的应用程序,用Python/PyQt编写.我打算做一些重构来真正将UI与核心分开,主要是因为还没有任何真正的测试,而且显然必须改变.
已经存在一些分离,我认为我已经以正确的方式做了很多事情,但它远非完美.两个例子,告诉你什么样的事情困扰着我:
当用户右键单击数据对象的表示时,弹出的上下文菜单由数据对象创建,尽管此数据对象(实质上是数据库行的ORM表示)应该与UI无关.
当某些内容被写入数据库但写入失败时(例如,因为数据库记录被其他用户锁定),会向用户呈现经典的"重试/中止"消息框.此对话框由数据提供程序*创建,但提供程序显然不应具有任何UI功能.显然,提供者可以提出异常或以其他方式指示失败,并且UI可以捕获并相应地采取行动.
*这是我用于对象的词,它基本上代表数据库表并在其数据对象和数据库引擎之间进行调解; 我不确定这是否通常被称为"提供者"
我没有测试经验,所以我不容易"感觉"可测性问题等,但在我开始之前,必须进行一些重组.
没有复杂的业务逻辑(它主要只是CRU D,是的,即使没有D),这将比重写更重组,所以我并不真正关心引入这个问题中讨论的回归错误.
我的计划是开始重构这个想法,因为UI部分很容易被删除,例如,由Web前端或基于文本的界面而不是Qt界面替换.另一方面,Qt本身仍将被核心使用,因为信号/插槽机制在很多地方使用,例如数据对象发出changed
信号来指示,你知道什么.
所以,我的问题:这是一种提高可测试性和可维护性的可行方法吗?还有其他任何评论,特别是考虑到Python吗?
据我所知,MVC中关注点分离的"正确"结构是为您的视图构建视图模型,并为您选择的存储库中的数据模型提供独立的数据模型.我开始尝试使用MongoDB,并且我开始认为在使用无架构的NO-SQL样式数据库时这可能不适用.我想将此场景呈现给stackoverflow社区,看看每个人的想法是什么.我是MVC的新手,所以这对我来说很有意义,但也许我忽略了一些......
以下是我讨论的示例:当用户想要编辑他们的个人资料时,他们会转到UserEdit视图,该视图使用下面的UserEdit模型.
public class UserEditModel
{
public string Username
{
get { return Info.Username; }
set { Info.Username = value; }
}
[Required]
[MembershipPassword]
[DataType(DataType.Password)]
public string Password { get; set; }
[DataType(DataType.Password)]
[DisplayName("Confirm Password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
[Required]
[Email]
public string Email { get; set; }
public UserInfo Info { get; set; }
public Dictionary<string, bool> Roles { get; set; }
}
public class …
Run Code Online (Sandbox Code Playgroud) 假设对于每个域实体,我都有一个为数据映射器提供API的存储库.例如,如果我有一个UserEntity,那么我会有一个UserRepository与UserMapper说话,以便在数据库中保存用户数据.
现在,假设一个表单在网页上提交,我的控制器知道它需要根据提交的信息创建一个新的UserEntity.
可以:
在那里做新的UserEntity(),并根据提交的表单数据运行所有必要的setter方法,然后将UserEntity传递给repo,后者将传递给mapper进行插入?
控制器创建UserEntity => Repo => Mapper => DB
将表单数据转换为数组,并将其传递给UserRepository,然后UserRepository运行新的UserEntity()和setter,并将其传递给mapper进行插入?
控制器传递用户数据=> Repo创建UserEntity => Mapper => DB
将数组传递给UserRepository,UserRepository将数组传递给映射器以获取新的UserEntity和插入?
控制器传递用户数据=> Repo传递用户数据=> Mapper创建UserEntity => DB
谁负责管理对象的创建?
我希望能够改变和传递我的UserEntity的某些部分,并且某些部分应该保持不变.
例如,我从不想改变我的UserEntity的id,但是电子邮件或密码之类的东西可能会经常更改,并且也可以被UserEntity之外的其他对象使用.
其中一个例子是创建UserEntity时.由于UserEntity在没有id的情况下不能存在,我的控制器可以创建一个UserData对象来标准化UserEntity属性.在映射器在db中创建实体之后,它将创建一个新的UserEntity并传入构造函数中的id和UserData对象.
当UserEntity需要电子邮件或密码等信息时,它只能查看其UserData.
看起来更便携,但这有点过分吗?有更好的解决方案吗?
注意
我认为这可能是好的原因:可变字段的值需要标准化......有时这些字段需要在实体本身之外传递.例如,在创建实体之前.通过创建一个可以传递的值对象,我们提供了一个标准化的点,可以从任何地方分配这些值,以及可以在实体外部传递的东西.
通过"标准化",我的意思是我的信息需要统一,无论它存在于何处.例如,email
需要总是n
长度和有效的格式,name
总是需要n
长度等.我的目标是,我希望能够在一个地方设置这些"规则"...并且因为这些UserEntity(可变的)的属性存在于实体本身之外,有时,它们可能在自己的值对象中独立存在.
可能的Noob警告: RoR的新功能
我试图在RoR中使用关注点.现在我只是写了一个非常简单的问题
#./app/controllers/concerns/foo.rb
module Foo
extend ActiveSupport::Concern
def somethingfoo
puts "Ayyyy! Foo"
end
end
Run Code Online (Sandbox Code Playgroud)
当我尝试在我的控制器中使用此问题时,我得到一个未定义的方法错误
#./app/controllers/foo_controller.rb
class FooController < ApplicationController
include Foo
def show
Foo.somethingfoo # undefined method 'somethingfoo' for Foo:Module
render plain: "Ohh no, It doesnt even show me because of the error above me"
end
end
Run Code Online (Sandbox Code Playgroud)
据我所知somethingfoo
应该被召唤但事实并非如此.我也试过somethingfoo
在一个included do ... end
块中定义关注但是这也不起作用.
有什么我想念的吗?控制器可以不用这样的问题吗?
我有一个ASP.NET 5 dnxcore解决方案,其中包含一些项目来区分我的逻辑:
现在我使用DI在我的API控制器的构造函数中调用我的服务:
private readonly IMyService _myService;
public Controller(IMyService myservice){ _myService = myService; }
Run Code Online (Sandbox Code Playgroud)
我核心中的服务也通过构造函数注入获取存储库:
private readonly IMyRepository _myRepo;
public MyService(IMyRepository myRepo){ _myRepo = myRepo; }
Run Code Online (Sandbox Code Playgroud)
目前我需要在我的API的启动类中定义我的DI容器以使其工作.
我的问题是,如何在我的Core-project中将存储库的DI容器的"构建"放入我的服务中.这样,我的API与我的服务使用Entity Framework这一事实松散耦合,因此我可以更改为mongodb,而无需更改我的API项目.
dependency-injection separation-of-concerns autofac repository-pattern asp.net-core
我有这个问题一直困扰着我一段时间.乍一看,它似乎是一个相当基本的问题,但似乎没有一个明确的答案.我提出这个问题的基础是一个通过SQLAlchemy使用PostgreSQL的Python项目,但使用的特定语言和工具并不重要.
基本问题是在使用ORM时放置逻辑?更具体地说,我的映射类的方法应该采用什么逻辑?
似乎有一些明确的案例,例如,属性的基本验证,例如最小长度或某些电子邮件地址是否属于ORM层(甚至是数据库本身).
一个简单的具体示例是有序的,因此我们假设我们正在存储用户对象,用户有一个电子邮件地址(这是他们唯一的ID),一个密码和一个激活ID.创建帐户时,会分配激活ID并将其邮寄给用户,然后使用该ID设置密码.
我们来看几个场景:
因此,当用户创建帐户时,我们需要运行一些基本验证:
当用户激活她的帐户时,我遇到了问题.我想验证提供的密码(例如足够长?足够的数字?),但模型应使用某种散列或加密算法存储密码.似乎有两种选择:
1:检查模型之外的激活ID和密码有效性,散列密码然后将User.password设置为散列,并将User.activation id设置为null(以指示活动帐户).
2:创建一个User.activate(activation_id,password)方法,该方法验证每个并相应地设置User.password和User.activation_id属性.
3:混合选项,执行选项1,但在设置密码时设置某种挂钩或事件激活_id = null(如果我们假设可以设置激活ID或密码,则此挂钩满足基本一致性属性该模型).
我在互联网上搜索过,有些消息来源谈论这个问题(包括一些SO问题),但没有人真正回答这个问题.
在ActiveSupport :: Concern上下文中访问包含类的受保护常量的最简单方法是什么?
示例类:
module Printable
extend ActiveSupport::Concern
private
def print_constant
puts MY_CONSTANT
end
end
class Printer
include Printable
def print
print_constant
end
private
MY_CONSTANT = 'Hello'.freeze
end
Run Code Online (Sandbox Code Playgroud)
此解决方案会产生错误:
NameError: uninitialized constant Printable::MY_CONSTANT
Run Code Online (Sandbox Code Playgroud)
我知道一种似乎有效的替代方案:
puts self.class::MY_CONSTANT
Run Code Online (Sandbox Code Playgroud)
但是,感觉不对.:-)
有更好的建议吗?
asp.net-mvc ×2
php ×2
python ×2
ruby ×2
architecture ×1
asp.net-core ×1
autofac ×1
model ×1
mongodb ×1
orm ×1
qt ×1
refactoring ×1
sqlalchemy ×1
unit-testing ×1
web ×1