走向ASP.NET架構設計——第四章:業務層分層架構(前篇)
在討論完四種模式之后,我將會和大家一起來看看DDD的一些知識。每種模式的講解,我都會用實例的形式給出完整的代碼,也希望大家多琢磨!
不是所有的應用程序都是一樣的,也不是所有的系統都需要用復雜的架構來組織業務邏輯。作為開發人員,我們必須清楚每一種業務邏輯組織的模式,這樣我們才能在需要的時候做出合適的選擇。
Transaction Script
這種組織業務邏輯的模式是最簡單,也是最容易理解的。Transaction Script模式就是用面向過程的方式來組織業務邏輯的。通常情況下,系統的一個流程就被實現為一個方法,然后把所有的這些方法組織在一起,放在一個靜態的manager類或者service 類中。實現流程的那個方法包含了業務邏輯的Check和Validation,數據的持久化以及其他的一些相關操作。也就是說,一個方法把所有的事情都做完了。當然,有時候這個大的方法還可能被拆成小的方法,便于重用。如下圖所示:
Transaction Script一個好處就是理解起來很簡單,尤其是當Team中的一個新成員來說,更是如此,因為他幾乎不用花什么時間,就能立刻明白這種組織業務邏輯的方式。每當來了一個新的需求的時候,要做的事情就是去加上一個或者一些新的方法來是實現這個需求,而其還不會影響其他已經存在的功能。
對于一個很小的或者基本山沒什么業務邏輯的系統來說,用Transaction Script模式組織業務邏輯還是很不錯的,而且對一個剛剛踏入IT的開發人員門檻也比較低。:當系統開始變大,業務邏輯開始變得復雜的時候Transaction Script的問題的出來了。最后的結果可能就是系統中存在大量的方法,而且這些方法中到處都是重復的代碼。有的時候,我們可以提煉出一些業務邏輯的驗證代碼組織為方法,但是我們去很難提煉出一些在流程上相識的代碼,即使兩個流程只有一點點的不同。如果系統的需求稍微一邊,導致流程變了一點點,那么很多的方法就要改動,而且我們還得在系統中去找出那些相似的流程代碼,然后修改,萬一哪個方法沒有找出,后果可想而知。
下面我們就用一個人事請假管理系統為例子來看看Transaction Script是如何實現的。因為Transaction Script很簡單,所以下面的代碼也只是用于演示,大家理解就行了。
{
public static bool BookHolidayFor(int employeeId, DateTime From, DateTime To)
{
bool booked = false;
TimeSpan numberOfDaysRequestedForHoliday = To - From;
if (numberOfDaysRequestedForHoliday.Days > 0)
{
if (RequestHolidayDoesNotClashWithExistingHoliday(employeeId, From, To))
{
int holidayAvailable = GetHolidayRemainingFor(employeeId);
if (holidayAvailable >= numberOfDaysRequestedForHoliday.Days)
{
SubmitHolidayBookingFor(employeeId, From, To);
booked = true;
}
}
}
return booked;
}
private static int GetHolidayRemainingFor(int employeeId)
{
// ...
}
public static List<EmployeeDTO> GetAllEmployeesOnLeaveBetween(
DateTime From, DateTime To)
{
// ...
}
public static List<EmployeeDTO> GetAllEmployeesWithHolidayRemaining()
{
// ...
}
}
一眼看上去,我們就很容易理解這種面向過程的方式:系統中的所有的用例都被組織成了一個個的方法。例如在BookHolidayFor方法中就做了很多的事情:查詢和持久化數據,是否允許請教的業務邏輯的實現等。
如果在一個很簡單的,業務很少的系統中采用這種方式還是可以的,隨著業務邏輯的越來越復雜,我們就得重新考慮下這種組織邏輯的方式,越早發現,越早做出改變,以后的成本將會越小。
Active Record
當我們系統中的業務類和數據庫中的表存在一 一對應關系的時候,尤其是每一個業務對象都代表了數據表中的一行,并且業務對象還要包含一些CRUD方法的時候,Active Record模式是比較合適的。下面我們來看看一個Blog系統例子:Post:就表示發表的一篇文章,Comment:評論。
在Active Record模式中,每一個業務對象各自負責自己的數據持久化邏輯和相關的業務邏輯。正如前面提到的:Active Record模式很適合那種“業務對象和數據表一 一對應”的簡單的應用程序,例如Blog,Forum等系統。因為在Active Record模式中,每一個業務類和表都有對應的關系,而且業務類都包含了CRUD的操作,那么我們可以使用一些代碼生成工具來加速我們的開發,而且有些好的代碼生成工具還包含了一些數據庫的邏輯驗證代碼來確保我們把正確的數據保存到數據庫中。
下面我們就用一個例子來看看這種模式是如何應用的。例子還是采用之前談到的Blog系統。
注:在系列文章中,我將會采用各種不同的技術來實現系統,在下面的這個例子,我將會采用NHibernate為例子。大家理解代碼就行了,然后按照自己的理解選用其他的技術。
首先我們將會創建一個名為:ASPPatterns.Chap4.ActiveRecord的新的解決方案,然后添加一些項目,并且添加一些必要的dll引用,如下:
然后我們在ASPPatterns.Chap4.ActiveRecord.UI.MVC中添加一個數據庫:Blog.mdf,然后添加兩張表:
Posts表:
Comments表:
下面我們就來看看ASPPatterns.Chap4.ActiveRecord.Model中的Comment類:
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Castle.ActiveRecord;
namespace ASPPatterns.Chap4.ActiveRecord.Model
{
[ActiveRecord("Comments")]
public class Comment : ActiveRecordBase<Comment>
{
[PrimaryKey]
public int Id { get; set; }
[BelongsTo("PostID")]
public Post Post { get; set; }
[Property]
public string Text { get; set; }
[Property]
public string Author { get; set; }
[Property]
public DateTime DateAdded { get; set; }
}
}
代碼中,可以看出Comment類和數據表Comments是對應的,而且在Comment加上了一些Attribute也表明這個屬性和數據表中的列式對應的。我們的例子中采用了Castle Active Record框架,這個框架將會根據這些Attribute來自動生成一些需要的SQL。和我們用Linq To Sql 的原理一樣。
下面的Post類也是一樣的:
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Castle.ActiveRecord;
using Castle.ActiveRecord.Queries;
namespace ASPPatterns.Chap4.ActiveRecord.Model
{
[ActiveRecord("Posts")]
public class Post : ActiveRecordBase<Post>
{
[PrimaryKey]
public int Id { get; set; }
[Property]
public string Subject { get; set; }
[Property]
public string Text { get; set; }
public string ShortText
{
get {
if (Text.Length > 20)
return Text.Substring(0, 20) + "...";
else
return Text;
}
}
[HasMany]
public IList<Comment> Comments { get; set; }
[Property]
public DateTime DateAdded { get; set; }
public static Post FindLastestPost()
{
SimpleQuery<Post> q = new SimpleQuery<Post>(@"from Post p order by p.DateAdded desc");
return (Post)q.Execute()[0];
}
}
}
因為這個Blog系統比較的簡單,而且邏輯不多,如果需要,大家可以再Model中加入更多的邏輯。下面我們就顯示這些文章和評論,在顯示層我們采用的ASP.NET MVC框架。我們創建一個BlogController來負責處理有關操作:
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;
using ASPPatterns.Chap4.ActiveRecord.Model;
namespace ASPPatterns.Chap4.ActiveRecord.UI.MVC.Controllers
{
public class BlogController : Controller
{
// GET: /Blog/
public ActionResult Index()
{
Post[] posts = Post.FindAll();
if (posts.Count() > 0)
{
ViewData["AllPosts"] = posts;
ViewData["LatestPost"] = Post.FindLastestPost();
return View();
}
else
{
return Create();
}
}
// POST: /Blog/
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult CreateComment(string Id, FormCollection collection)
{
int postId = 0;
int.TryParse(Id, out postId);
Post post = Post.Find(postId);
Comment comment = new Comment();
comment.Post = post;
comment.Author = Request.Form["Author"];
comment.DateAdded = DateTime.Now;
comment.Text = Request.Form["Comment"];
comment.Save();
return Detail(post.Id.ToString());
}
// GET: /Blog/Detail/1
public ActionResult Detail(string Id)
{
ViewData["AllPosts"] = Post.FindAll();
int postId = 0;
int.TryParse(Id, out postId);
ViewData["LatestPost"] = Post.Find(postId);
return View("Index");
}
// GET: /Blog/Create
public ActionResult Create()
{
return View("AddPost");
}
// POST: /Blog/Create
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(FormCollection collection)
{
Post post = new Post();
post.DateAdded = DateTime.Now;
post.Subject = Request.Form["Subject"];
post.Text = Request.Form["Content"]; ;
post.Save();
return Detail(post.Id.ToString());
}
}
}
例子很簡單,大家看看就明白了。 然后添加對應的View:
一個是顯示文章要用的:
一個是添加文章用的:
頁面的Source代碼我就不列出來,大家可以下載代碼自己看看,也是比較的簡單。 然后我們需要到Global.asax文件中去加一些代碼:
Route配置:
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Blog", action = "Index", id = "" } // Parameter defaults
);
}
啟用Castle 的一些代碼。
{
RegisterRoutes(RouteTable.Routes);
IConfigurationSource source = ConfigurationManager.GetSection("activeRecord") as IConfigurationSource;
Castle.ActiveRecord.ActiveRecordStarter.Initialize(source, typeof(Post), typeof(Comment));
}
最后在web.config 中加上NHibernate的一些配置就行了,這里不列出來,大家看代碼!(見諒)。
從上面的一些工作量可以看出,用相應的Frameword來實現blog系統還是比較的快的。而且上面的例子中,業務類和表的結構很一致,這也是Active Record的特點和優勢。但是如果業務類和數據表的結構不一致,而且邏輯也變復雜了,也就是出現了“抗阻不匹配”。如果還在這中模式上面堅持,會使后面的開始過程和代碼維護變得困難,那么我們就得采用Domain Model模式來組織業務邏輯。
上面的例子講的很粗糙,因為上面的兩種模式本身還是比較容易理解的,到了Domain Model模式的講解的時候,會更加的詳細。
今天就到這里了,還是希望多多見諒,支持!謝謝啊!代碼下載