你有什麼有一種變相的命令的一個很好的例子。很高興你在這裏做的是你的服務方法已經在一個參數DoSomethingData
。這是您的命令消息。
什麼你錯過這裏是在命令處理程序的一般抽象:
public interface ICommandHandler<TCommand>
{
void Handle(TCommand command);
}
隨着重構的一點點,你的服務的方法是這樣的:
// Vanilla dependency.
ICommandHandler<DoSomethingData> doSomethingHandler;
public void DoSomething(DoSomethingData data)
{
this.doSomethingHandler.Handle(data);
}
,當然還有您需要執行ICommandHandler<DoSomethingData>
。在你的情況下,它看起來就像這樣:
public class DoSomethingHandler : ICommandHandler<DoSomethingData>
{
public void Handle(DoSomethingData command)
{
// does the actual something
DoSomethingInternal(command);
}
}
現在,你可能會想,怎麼樣的橫切關注你喜歡實現參數驗證,罐火,出版渠道狀態更新和錯誤處理。好吧,他們都是交叉問題,你的WCF服務類和你的業務邏輯(DoSomethingHandler
)都不應該擔心這一點。
有幾種應用面向方面編程的方法。有些人喜歡使用PostSharp等代碼編織工具。這些工具的缺點是它們使得單元測試變得更加困難,因爲你將所有的交叉問題編織在一起。
第二種方法是通過使用攔截。使用動態代理生成和一些反射。然而,我更喜歡這種變化,那就是應用裝飾器。關於這一點的好處是,這是我的經驗,應用橫切關注的最乾淨的方式。
讓我們來看看你的驗證一個裝飾:
public class WcfValidationCommandHandlerDecorator<T> : ICommandHandler<T>
{
private IValidator<T> validator;
private ICommandHandler<T> wrapped;
public ValidationCommandHandlerDecorator(IValidator<T> validator,
ICommandHandler<T> wrapped)
{
this.validator = validator;
this.wrapped = wrapped;
}
public void Handle(T command)
{
if (!this.validator.ValidateArgument(command))
{
throw new FaultException(...);
}
// Command is valid. Let's call the real handler.
this.wrapped.Handle(command);
}
}
由於這WcfValidationCommandHandlerDecorator<T>
是一個通用的類型,我們可以繞到它的每一個命令處理程序。例如:
var handler = new WcfValidationCommandHandlerDecorator<DoSomethingData>(
new DoSomethingHandler(),
new DoSomethingValidator());
而且你可以爲輕鬆地創建處理任何拋出的異常裝飾器:
public class WcfExceptionHandlerCommandHandlerDecorator<T> : ICommandHandler<T>
{
private ICommandHandler<T> wrapped;
public ValidationCommandHandlerDecorator(ICommandHandler<T> wrapped)
{
this.wrapped = wrapped;
}
public void Handle(T command)
{
try
{
// does the actual something
this.wrapped.Handle(command);
_publicationChannel.StatusUpdate(new Info
{
Status = transitionResult.NewState
});
}
catch (FaultException<MyError> faultException)
{
if (faultException.Detail.ErrorType == MyErrorTypes.EngineIsOffline)
{
TryFireEvent(MyServiceEvent.Error, faultException.Detail);
}
throw;
}
}
}
你怎麼看我剛剛結束你的代碼在這個裝飾?我們可以再次使用這個裝飾包裹原文:
var handler =
new WcfValidationCommandHandlerDecorator<DoSomethingData>(
new WcfExceptionHandlerCommandHandlerDecorator<DoSomethingData>(
new DoSomethingHandler()),
new DoSomethingValidator());
當然,這一切似乎像一個可怕的很多的代碼,如果你有比是一個單一的WCF服務的方法,這可能是矯枉過正。但如果你有十幾個,它開始變得非常有趣。如果你有幾百?那麼..如果你不使用這種技術,我不想成爲開發人員維護代碼庫。
因此,經過幾分鐘的重構後,您將獲得僅取決於ICommandHandler<TCommand>
接口的WCF服務類。所有的交叉問題將被放置在裝飾器中,當然,所有事情都通過DI庫連接在一起。我想你知道幾個;-)
當你這樣做,有可能是一件事,你可以改善,因爲所有的WCF服務類將開始尋找乏味相同:
// Vanilla dependency.
ICommandHandler<FooData> handler;
public void Foo(FooData data)
{
this.handler.Handle(data);
}
它將開始無聊寫新的命令和新的處理程序。你仍然有你的WCF服務來維護。
你能做什麼,而不是,是創建一個類的WCF服務有一個方法,像這樣:
[ServiceKnownType("GetKnownTypes")]
public class CommandService
{
[OperationContract]
public void Execute(object command)
{
Type commandHandlerType = typeof(ICommandHandler<>)
.MakeGenericType(command.GetType());
dynamic commandHandler = Bootstrapper.GetInstance(commandHandlerType);
commandHandler.Handle((dynamic)command);
}
public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider provider)
{
// create and return a list of all command types
// dynamically using reflection that this service
// must accept.
}
}
現在你已經是一個單一的方法WCF服務,它永遠不會改變。 ServiceKnownTypeAttribute
指向GetKnownTypes
。 WCF將在啓動時調用此方法以查看它必須接受的類型。當您根據應用程序元數據返回列表時,它允許您向系統添加和刪除命令,而無需更改WCF服務中的單行。
您可能會偶爾添加新的WCF特定裝飾器,而這些裝飾器通常應放置在WCF服務中。其他裝飾器可能會更通用,可能會放置在業務層本身。例如,它們可能會被您的MVC應用程序重用。
你的問題有點關於CQRS,但我的答案與它無關。那麼......沒有什麼是多餘的。 CQRS廣泛使用這種模式,但CQRS更進一步。 CQRS是關於協作域的,它強制你排隊命令並且異步處理它們。另一方面,我的回答只是應用了設計原理。無處不在。不僅在協作領域。
如果您想了解更多關於此的信息,請閱讀我關於申請command handlers的文章。之後,繼續閱讀my article about applying this principle to WCF services。我的回答是這些文章的摘要。
祝你好運。
哇,非常感謝你,史蒂文,爲你的難以置信的詳細答案!我想過實現'ICommand/ICommandHandler'模式,我只是想確保這是處理這種情況的「慣用」方式。我很高興你讓我放心,我是對的:) – 2013-02-12 12:11:47
這只是留下了我是否應該封裝狀態機查詢,不知何故的問題。但在這一點上,我將把它留在服務中。 – 2013-02-12 12:25:44
我真的不能對此發表評論。我不知道你的狀態機是什麼。但我敢打賭,你可以將它封裝在裝飾器中。更好的是,當你掌握了這種模式時,你會發現你可以對代碼進行各種新的改進。歡迎來到你的新生活;-) – Steven 2013-02-12 12:28:24