Rafy 框架:領域控制器

Rafy

本文簡要說明如何使用 Rafy 框架中的領域控制器。

簡介

領域控制器是 Rafy 框架中用於封裝領域邏輯的主要方式。

在控制器中,開發者可以封裝大量的業務邏輯,並向外暴露業務接口。內部的邏輯在實現時,往往調用一個或多個實體倉庫的 CDUQ 方法來實現。

示例

以下代碼爲 Rafy.Accounts 帳戶插件 中 AccountController 類型的真實代碼。

/// <summary>
/// 帳戶插件的領域控制器。
/// </summary>
public class AccountController : DomainController
{
	/// <summary>
	/// 註冊指定的用戶。
	/// </summary>
	/// <param name="user"></param>
	/// <returns></returns>
	[ControllerLogic]
	public virtual Result Register(User user)
	{
		if (user == null) throw new ArgumentNullException("user");
		var userNameAsId = _identityMode.HasFlag(UserIdentityMode.UserName);
		if (userNameAsId && string.IsNullOrEmpty(user.UserName)) return new Result(ResultCodes.RegisterUserNameInvalid, "用戶名不能爲空。");
		var emailAsId = _identityMode.HasFlag(UserIdentityMode.Email);
		if (emailAsId && !TextFormatter.ReEmail.IsMatch(user.Email)) return new Result(ResultCodes.RegisterEmailInvalid, "郵箱格式不正確。");
		if (!userNameAsId && !emailAsId) throw new InvalidProgramException("!userNameAsId && !useEmailAsId");

		//驗證其它屬性。
		var brokenRules = Validator.Validate(user);
		if (brokenRules.Count > 0) return new Result(ResultCodes.RegisterPropertiesInvalid, brokenRules.ToString());

		//檢查用戶名、郵箱的重複性。
		var repo = RF.ResolveInstance<UserRepository>();
		var criteria = new CommonQueryCriteria();
		criteria.Concat = BinaryOperator.Or;
		if (userNameAsId)
		{
			criteria.Add(new PropertyMatch(User.UserNameProperty, user.UserName));
		}
		if (emailAsId)
		{
			criteria.Add(new PropertyMatch(User.EmailProperty, user.Email));
		}
		var exists = repo.GetFirstBy(criteria);
		if (exists != null)
		{
			if (emailAsId && exists.Email == user.Email)
			{
				return new Result(ResultCodes.RegisterEmailDuplicated, string.Format("註冊失敗,已經存在郵箱爲:{0} 的用戶。", user.Email));
			}
			else
			{
				return new Result(ResultCodes.RegisterUserNameDuplicated, string.Format("註冊失敗,已經存在用戶名爲:{0} 的用戶。", user.UserName));
			}
		}

		//保存這個用戶
		user.PersistenceStatus = PersistenceStatus.New;
		repo.Save(user);

		this.OnRegisterSuccessed(user);

		return true;
	}

	/// <summary>
	/// 註冊成功的事件。
	/// </summary>
	public event EventHandler<AccountEventArgs> RegisterSuccessed;

	/// <summary>
	/// 註冊成功的事件。
	/// </summary>
	/// <param name="user"></param>
	protected virtual void OnRegisterSuccessed(User user)
	{
		var handler = this.RegisterSuccessed;
		if (handler != null) handler(this, new AccountEventArgs(user));
	}
}

調用方的代碼如下:

var controller = DomainControllerFactory.Create<AccountController>();

var res = controller.Register(new User
{
    UserName = "hqf",
    RealName = "hqf",
    Password = controller.EncodePassword("hqf")
});

通過 DomainControllerFactory 來創建一個控制器(也可用簡寫 DCF),即可調用其中的方法。

特點

  • 支持本地調用,也支持分佈式調用

    領域控制器是除了倉庫查詢以外,提供分佈式數據傳輸的另一機制。控制器的調用,支持本地調用,也支持分佈式調用。詳見:部署

  • 無狀態

    領域控制器本身應該是無狀態的。每次使用工廠創建時,都會創建一個新實例。特殊情況下,如果需要傳遞狀態,需要對屬性添加 [ControllerClientSettings] 標記。

  • 支持領域控制器事件及依賴管理

    詳見後文。

  • 支持使用接口來定義控制器契約。參見:IDomainControllerContract 接口。

遠程調用

DomainController 中,所有可遠程調用的方法,都需要滿足:一、標記爲虛方法;二、添加 [ControllerLogic] 標記。工廠會爲在運行時創建控制器的子類,並這些方法實現遠程調用。

所以,此類方法需要注意,參數及返回值應該都是要支持序列化的。否則會在遠程調用時失敗。

領域控制器事件

各業務模塊可以分別定義大量的領域控制器,而模塊之間的業務,往往需要進行交互。除直接的調用關係以外,領域控制器還提供了事件依賴及管理功能。

例如,我們往往希望在用戶註冊成功後,各業務模塊(例如博客模塊)再額外註冊一些其它內容。這時,我們又不希望修改用戶的註冊代碼。那麼我們可以在博客模塊的領域控制器中,指定該控制器依賴 AccountController,這時再監聽 RegisterSuccessed 事件添加自己的業務邏輯。

下面示例代碼中,基礎庫存模塊與入庫管理插件,後者依賴前者。代碼展示了,庫存業務插件的 StockChanged 事件發生時,入庫模塊會發生一些特定的邏輯。

//業務插件一:庫存模塊
public class StockController : DomainController
{
    public event EventHandler StockChanged;

    protected virtual void OnStockChanged()
    {
        var handler = this.StockChanged;
        if (handler != null) handler(this, EventArgs.Empty);
    }
}

//業務插件二:入庫管理插件
public class RecieveController : DomainController
{
    static RecieveController()
    {
        Depend<RecieveController>().On<StockController>();
    }

    protected override void OnAlwaysDependon(DomainController controller)
    {
        var sc = controller as StockController;
        if (sc != null)
        {
            sc.StockChanged += OnStockChanged;
        }
    }

    private void OnStockChanged(object sender, EventArgs e)
    {
        //根據庫存變化信息,來實現特定功能
    }
}

PS:該文已經納入《 Rafy 用戶手冊》中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章