业务逻辑(BLL)层的组织,长期以来一直是个困惑。打从开始引入ORM后,BLL层不再出现SQL语句和存储过程,感觉思路清晰了不少,现在自己对BLL结构认识大致上定型,一般根据不同方面的业务逻辑,对应不同的命名空间,亦即不同的文件夹,每个文件夹下可能有多个Helper类。对于缓存、日志、邮件等通Helper类,放在Common目录下。
这是传统的三层架构,如果使用SOA的话,可能多一个服务层,一般来说,项目分层不要超过四层。
下面说说创建Helper基类的目的,因为各个Helper类,尤其是在Web开发中,都面临一些类似的问题:
1、如何和数据打交道。有了ORM不能替代我们对性能的思考,比如有时候执行完一个方法,就向数据源提交更改,有时候需要和其它方法一起执行完后再提交更改,并且共享一个数据访问连接。
2、如何和HttpContext打交道。显然,到处写HttpContext.Current大大增加了系统的耦合性,如果将HttpContext完全排斥在业务逻辑层外,又会UI层和BLL层代码变得复杂。所以封装HttpContext的操作是必要的,同时也对单元测试性提供方便。
3、如何处理异常和错误。有些异常不影响程序正常处理,如提醒邮件发送失败,要捕获它们,写入系统日志,或给予用户提示。令请求无法正常执行的异常则无需捕获,如数据库连接出错。不同业务逻辑处理,可能会设置一些前提条件,如果因不满足某个条件导致执行失败,也应该有相应的提示。在返回值中表示不同的提示类型,并不足以表示一些复杂多变的场景,比如批量转账,我们应将所有未成功的转账信息集中返回给前端页面。
所以,个人认为一般的业务处理方法,只需返回成功与否即可,或是void。而将执行中的异常、消息、警告,都在Helper基类的属性中交待。
一个基本的Helper基类,代码如下:
/// <summary> /// 所有Helper的基类 /// </summary> class BaseHelper<T> { private HttpContext context; /// <summary> /// Web请求上下文 /// </summary> public HttpContext Context { get { if (context == null) context = HttpContext.Current; return context; } set { context = value; } } List<T> details; /// <summary> /// 处理细节 /// </summary> protected List<T> Details { get { if (details == null) details = new List<T>(); return details; } } DBEntities db; /// <summary> /// 涉及数据操作 /// </summary> protected DBEntities DB { get { if (db == null) db = new DBEntities(); return db; } } /// <summary> /// 是否在方法内保存 /// </summary> protected bool AutoSave { get; set; } /// <summary> /// 保存到数据源 /// </summary> protected int Save() { if (AutoSave) return DB.SaveChanges(); else return 0; } }
另外,还有一些通用的课题,比如缓存同步、权限控制等,也感到了不少局限性,留待以后进一步解决吧。这是最后一个传统的WebForm项目,以后开发方向就以MVC为主了。