(下面是發在javaeye上的帖子,因為覺的還有點意思,轉到blog來,關于Domain和AOSD已經有了一些新的想法)
應用Domain開發的系統,通常把邏輯放在Domain Service層中,而Domain Service做兩個工作:
1. 和表現層通信,表現為把表現層的平面數據(VO)轉換為相關聯的Domain對象,把Domain對象計算的結果轉換成平面數據(VO)返回給表現層;
2.根據Use Case完成商業邏輯的調度。
以下主要討論Use Case的內容。
通常Use Case所描述的Business Flow分為四種:Basic Flow,Alternate Flow,Exception Flow和Extension Flow。
雖然Business Flow可能包含很多領域對象,由于每個use case的目標帶有濃厚的領域邏輯,因而可以通過分析提煉出一個主domain對象。然后重組轉換來自BA或者PM的BP設計文檔,使其中的Basic Flow基于主domain對象,而把 Alternate Flow,Exception Flow和 Extension Flow基于其它的Domain Service和Domain Object(當然包括Util objects), 最后利用AOP把Alternate Flow, Exception Flow和Extension Flow 與Basic Flow在Service層組織起來。
使用AOP來組織Use Case時,與使用AOP組織技術問題(比如日志,權限檢查和事務處理等)不同。
在AOP組織技術問題時,我們不關心join point的目標對象和目標方法以及入口參數。比如:
public class BankServiceImpl implements BankService{
public void transfer(UserAccount src, UserAccount dist,
BigDecimal amount)throws Exception{
src.subtract(amount);
dist.add(amount);
}
//Other code goes here
}
<
bean id
=
"
BankService
"
class
=
"
org.
springframework.transaction.interceptor.TransactionProxyFactoryBean
"
>
<
property name
=
"
transactionAttributes
"
>
<
props
>
<
prop key
=
"
transfer
"
>
PROPAGATION_REQUIRED
</
prop
>
</
props
>
</
property
>
</
bean
>
我們不關心參數,或者在一些方法重載的地方利用參數來識別區分我們的方法入口。
但當我們利用AOP來組織Use Case時我們關心目標對象和目標方法以及入口參數。因為AOP所要織入的方法是另一個Use Case是另一個Biz Flow。(這個在AOSD中顯示討論的不多,只有在12章12.4.中有提到)
比如我們要在轉帳成功后發手機短信通知客戶。那么在沒有用AOP代碼中我們這樣寫:
public class BankServiceImpl implements BankService{
public void transfer(UserAccount src, UserAccount dist,
BigDecimal change)throws Exception{
src.subtract(change);
dist.add(change);
SMSService.sendSMS(src, change);
SMSService.sendSMS(dist, change);
}
//Other code goes here
}
Public class SMSService {
public static void sendSMS(UserAccount user, BigDecimal change){
Long phone = user.getPhoneNumber();
BigDecimal balance = user.getBalance();
send(phone, change, belance)
}
private static void send(Long phone, change, balance){

}
}
事實用User Case的觀點分析,發送短信通知是另一個use case,是轉帳這個use case的extend flow。用AOP的方法應該如下:
public class BankServiceEx {
public static void notify(UserAccount src, BigDecimal change) {
SMSService.sendSMS(src, change);
}
//Other code goes here
}
public aspect BankServiceAspect {
pointcut transfer():call(void BankService.transfer(..));
after(UserAccount src, UserAccount dist, BigDecimal change) returning : transfer() && args(src, dist, change){
BankServiceEx.notify(src, change);
BankServiceEx.notify(dist, change);
}
}

這樣我們完成了兩個用例的分離,兩個用例獨立,可以重用和測試。比如上述短信通知用例其實可以被重用到其它情況如:存款,消費,以及銀行分紅等等。
不過可能面臨一個情況是,兩個獨立用例的代碼部分都可能用到某個對象, 那么在兩個用例中可能重復一部分代碼。雖然從概念上看,不應該重復(在使用用舊的方法實現時不會重復),但從不同use case看,這個重復是值得的。曾經考慮利用代碼生成,直接獲得Local Variable,這樣可以減少重復,但是這個想法是錯誤的,不僅僅是實現上的困難,更重要在于,分離出的發送短信用例便綁定了轉帳用例,依賴于轉帳用例,而無法獨立重用和測試。
這樣,對象、方法以及方法參數構成了一個完整的pointcut,成為不同用例切片的共同入口,相當于一個占位符。這個時候就需要不同的用例實現人員協調好該入口。
應用Domain開發的系統,通常把邏輯放在Domain Service層中,而Domain Service做兩個工作:
1. 和表現層通信,表現為把表現層的平面數據(VO)轉換為相關聯的Domain對象,把Domain對象計算的結果轉換成平面數據(VO)返回給表現層;
2.根據Use Case完成商業邏輯的調度。
以下主要討論Use Case的內容。
通常Use Case所描述的Business Flow分為四種:Basic Flow,Alternate Flow,Exception Flow和Extension Flow。
雖然Business Flow可能包含很多領域對象,由于每個use case的目標帶有濃厚的領域邏輯,因而可以通過分析提煉出一個主domain對象。然后重組轉換來自BA或者PM的BP設計文檔,使其中的Basic Flow基于主domain對象,而把 Alternate Flow,Exception Flow和 Extension Flow基于其它的Domain Service和Domain Object(當然包括Util objects), 最后利用AOP把Alternate Flow, Exception Flow和Extension Flow 與Basic Flow在Service層組織起來。
使用AOP來組織Use Case時,與使用AOP組織技術問題(比如日志,權限檢查和事務處理等)不同。
在AOP組織技術問題時,我們不關心join point的目標對象和目標方法以及入口參數。比如:



















我們不關心參數,或者在一些方法重載的地方利用參數來識別區分我們的方法入口。
但當我們利用AOP來組織Use Case時我們關心目標對象和目標方法以及入口參數。因為AOP所要織入的方法是另一個Use Case是另一個Biz Flow。(這個在AOSD中顯示討論的不多,只有在12章12.4.中有提到)
比如我們要在轉帳成功后發手機短信通知客戶。那么在沒有用AOP代碼中我們這樣寫:























事實用User Case的觀點分析,發送短信通知是另一個use case,是轉帳這個use case的extend flow。用AOP的方法應該如下:


















這樣我們完成了兩個用例的分離,兩個用例獨立,可以重用和測試。比如上述短信通知用例其實可以被重用到其它情況如:存款,消費,以及銀行分紅等等。
不過可能面臨一個情況是,兩個獨立用例的代碼部分都可能用到某個對象, 那么在兩個用例中可能重復一部分代碼。雖然從概念上看,不應該重復(在使用用舊的方法實現時不會重復),但從不同use case看,這個重復是值得的。曾經考慮利用代碼生成,直接獲得Local Variable,這樣可以減少重復,但是這個想法是錯誤的,不僅僅是實現上的困難,更重要在于,分離出的發送短信用例便綁定了轉帳用例,依賴于轉帳用例,而無法獨立重用和測試。
這樣,對象、方法以及方法參數構成了一個完整的pointcut,成為不同用例切片的共同入口,相當于一個占位符。這個時候就需要不同的用例實現人員協調好該入口。