我正在努力设计(或过度设计)我正在做的网络项目。
我有一个MyProject.Data,MyProject.Business和MyProject.Web DLL。
我的数据层(基于EF6)包含我的实体db context。
我的业务层包含我的存储库(可能不是真正的仓库模式)。
我已将IdentityFramwork添加到Web项目中,而cource则创建了ApplicationUser。我的数据层中已有用户POCO。我想将Application用户移动到Data层,因此我可以将它与其他实体一起使用。
实现此目的的一种方法是让我的Data.User扩展IdentityUser,并使我的Data.MyContext扩展IdentityDbContext。这导致数据层与asp.net.identity框架强烈耦合,这种框架并不是很正确。
这里的最佳做法是什么?
答案 0 :(得分:1)
我不建议User
延长ApplicationUser
。我认为ApplicationUser
只是用户可以访问您的应用程序的方式(通过电子邮件登录并通过Facebook,谷歌)。您的用户实体属于您的域,因此您不需要密码(作为示例)。实际上,单个User
可以关联许多ApplicationUser,为什么同一个用户不能使用不同帐户登录?
可以让您的数据层引用Identity的dll,您在项目中多久更改一次?我真的从来没有见过这种情况,一旦你把一个项目运行到一个已定义的框架,你很少改变它,当然你的应用程序可能会增长,但你很少需要改变这样的事情。这样,您的数据层不会耦合到Web项目,而是耦合到库(例如 sunil )。 AspNetUser的表将在那里,但WPF项目不一定需要将其用作登录方法
此外,我建议您将实体保留在业务层(我也建议不要在那里使用DataAnnotations
),然后制作数据层使用Fluent API将它们映射到实体框架,并将EF封装在您的具体存储库中,该存储库应位于数据层中,只需从业务层
使您的DBContext扩展IdentityDbContext,但不要让您的用户扩展ApplicationUser,您可以使ApplicationUser具有User。每当新用户登录您通常创建新ApplicationUser的应用程序时,您还可以创建User
并与之关联。一旦记录,该用户可以将另一个外部登录关联到他的帐户(这意味着许多ApplicationUser到同一个用户)。
下面的代码示例我没有得到任何现有的来源,我刚刚发明了用于分析
业务层 - 不应该知道其他层,它是最简单的,应该表达您的真实业务并成为无所不在的语言的一部分
public class User: ITrackable
{
public int ID { get; protected set; }
public string Name { get; set; }
public string Description { get; set; }
public UserStatus Status { get; set; }
public DateTime? Birthday { get; set; }
public virtual ICollection<Address> Addresses { get; protected set; }
public string LastKnownPlace { get; set; }
public virtual bool IsPremium()
{
// some logic
}
public string GetTrackingIdentification()
{
// gets some unique key representing this object. Shouldn't be ID, since I might track other objects, soon IDs would dup for different objects...
}
}
public interface ITrackable
{
string GetTrackingIdentification();
string LastKnownPlace { get; set; }
}
public interface ITrackingService<T> where T: ITrackable
{
void Track(IEnumerable<T> source);
}
public interface IUserTrackingService: ITrackingService<User>
{
IEnumerable<User> GetDeadbetUsersWithTracking();
}
public interface IUserRepository
{
IEnumerable<User> GetPremiumUsers();
IEnumerable<User> GetDeadbets();
}
基础架构层 - 应该处理应用程序请求的操作应该如何执行。它可以通过实现存储库来使用数据库持久性,可以使用实体框架或任何其他提供程序,也可以写入文本文件,排队任务,发送电子邮件,记录,也可以写入注册表,这些可以称为Infra服务。在这里,您应该参考业务层,您也可以引用Identity的dll。为了简单起见,我只是实现了存储库:
public class YourContext : IdentityDbContext<ApplicationUser>
{
DbSet<User> DomainUsers { get; set; }
DbSet<Address> Addresses { get; set; }
public YourContext(): base("DefaultConnection", throwIfV1Schema: false) { }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<IdentityRole>().Property(r => r.Name).HasMaxLength(200);
modelBuilder.Entity<ApplicationUser>().Property(a => a.UserName).HasMaxLength(200);
modelBuilder.Entity<User>().Property(u => u.Name).IsRequired().HasMaxLength(50);
}
}
服务层 - 将参考Businness和Infra层,实现业务服务。服务在这里是一个含糊不清的术语。有应用程序服务,域服务和Infra服务。 域服务是复杂/通用操作,它是业务逻辑的一部分,但不属于特定实体。应用程序服务是一种通过API,WCF等向外部世界公开操作的方法。我之前解释过的Infra服务。我将服务层理解为实现业务服务和公开给应用程序的层。由于Business不知道如何实现服务,因此可以委托给外部API。这样,企业仍然与一切脱钩。
public class UserTrackingService : IUserTrackingService
{
private IUserRepository repo;
// Injects an UserRepository to the service...
public UserTrackingService(IUserRepository repository)
{
this.repo = repository;
}
public IEnumerable<User> GetDeadbetUsersWithTracking()
{
var users = this.repo.GetDeadbets();
this.Track(users);
return users
}
public void Track(IEnumerable<User> source)
{
// This would use each ITrackable's (in this case each User) Identification to request some external API, get last known place and set to ITrackable's LastKnownPlace property...
api.Track(source);
}
}
应用层 - 可能会引用业务,红外/数据和服务层。在这里,您应该表达/定义应用可以执行的操作,而不是如何操作。应用程序只接收请求,对请求者进行身份验证,并保证他们有权执行所述操作。一切正常后,它会委托给服务/业务/下层。强烈建议使用DTO(或ViewModels)接收数据并将数据返回给外部世界。它可能会从service / business / infra图层接收域数据,转换为DTO并返回。这可以解释为控制器。
public class TrackedUserDTO
{
public string Name { get; set; }
public string MainAddress { get; set; }
public string LastKnownPlace { get; set; }
public decimal TotalDebt { get; set; }
public bool ShouldBeContacted { get; set; }
}
public class UserController : Controller
{
private IUserTrackingService service;
private IUserRepository repository;
// Injected - IoC
public UserController(IUserTrackingService service, IUserRepository repository)
{
this.service = service;
this.repository = repository;
}
public ActionResult Index()
{
return View();
}
[Authorize(Roles="Manager,Admin", ErrorMessage="You aren't allowed do see this content...")]
public ActionResult GetDeadbetUsersWithTracking()
{
IEnumerable<User> users = this.service.GetDeadbetUsersWithTracking();
IEnumerable<TrackedUserDTO> dtoUsers = Mapper.Map<IEnumerable<TrackedUserDTO>>(users);
return View(dtoUsers);
}
我强烈建议您阅读以下内容:
Creating Domain Services by Philip Brown
Services in Domain-Driven Design by Jimmy Bogard
为什么不在业务层内使用DTO?为了避免Anemic Domain Model (by Martin Fowler)