已經(jīng)學(xué)了兩年的MVC了,但是有些概念還是很模糊,希望能在這裡找到答案。
我曾經(jīng)看到有人說Controller不負責(zé)資料處理,全部交給我們的Service來處理,網(wǎng)頁前端傳回來是什麼資料類型,就直接把資料類型轉(zhuǎn)送到Service,然後由Service來處理;但又有另一個聲音說有時候會同時呼叫多個Service,如果在Controller就將物件封裝好了,就免於在Service的方法中多次封裝。
另外還有一個問題就是關(guān)於Controller與Service互動的問題。
我們?yōu)榱饲岸说目蛻艋恿己?,往往會透過Controller向前端傳回一些錯誤提示,例如使用者名稱已存在,使用者名稱和密碼不符等等??墒翘幚順I(yè)務(wù)邏輯我們是放在Service層,那麼如果把一個login(String username,String password)方法的回傳值設(shè)定為boolean就無法回傳多種錯誤,但是如果回傳String類型,就需要設(shè)定一些基本的字典。
我自己“奇思妙想”,我在Service中通過拋出我自定義的一些RuntimeException,然後在Controller中通過TryCatch來處理不同的錯誤,但是我自己認為這種拋出異常的方式不妥。最近就陷入了迷茫,馬上就要開始做下一個專案了。希望各位能幫我解答一下迷惑。
感謝。
小伙看你根骨奇佳,潛力無限,來學(xué)PHP伐。
我們專案裡分了三層:
表現(xiàn)層:Spring MVC,負責(zé)接收Http請求、展現(xiàn)(回)結(jié)果、簡單校驗
App層:提供應(yīng)用程式層的功能,例如匯入、匯出、複雜校驗
Domain層:處理業(yè)務(wù)邏輯,例如一些Service
呼叫順序是單項的:Controller->App->Domain
且感知關(guān)係為:Controller->App->Domain,即下層不感知上層
先回答你第一個問題:
你說的第一個問題我是否可以理解為,Http請求過來的參數(shù)不是Object,而是一堆基本型,但你的Service接收的參數(shù)是Object。正確的做法應(yīng)該是,在Controller將「生」參數(shù)轉(zhuǎn)換成Object對象,然後呼叫Service。
為何這樣是正確的?因為Service屬於Domain層,裡面是業(yè)務(wù)邏輯,其接受的參數(shù)應(yīng)該根據(jù)自己的需求而進行設(shè)計,不應(yīng)該考慮Web層過來的參數(shù)是什麼,這樣才可以做到在不同場景下重複使用。
舉個例子,你的Service應(yīng)該可以重複使用不同表現(xiàn)層環(huán)境下:
在一個Web程式中,使用者傳過來的參數(shù)是POST/GET形式給你的
在一個Web service程式中,使用者傳過來的參數(shù)是json或SOAP
在一個Swing程式中,使用者傳過來的參數(shù)就是字串
如果你將Service和具體某個表現(xiàn)層環(huán)境綁定,那麼其方法參數(shù)肯定不穩(wěn)定,結(jié)果就導(dǎo)致無法重複使用。
同理,Service的回傳值也不應(yīng)該和特定場景綁定。
在Spring MVC層面,Controller可以很方便的把參數(shù)轉(zhuǎn)換成Object,相關(guān)文件
第二個問題:
這個問題可以分為三個:
1)簡單的校驗例如參數(shù)長度限制、非空判斷等在哪裡做?
簡單校驗利用Spring MVC的自身提供的機製做,相關(guān)文檔,相關(guān)文檔
2)和Service本身的業(yè)務(wù)邏輯平行的校驗在哪裡做,例如用戶下單時判斷其是否帳號被禁用
我傾向於將這些邏輯校驗放在App層做,Controller調(diào)用App,App調(diào)用兩個不同的Service,將業(yè)務(wù)編織起來
3)和Service本身有關(guān)的業(yè)務(wù)邏輯校驗怎麼做
你舉的是登入的例子,用異常告知呼叫方(Controller)處理結(jié)果沒有任何問題。你也可以豐富Service的回傳值達到這個目的,不過需要注意的是,Service的回傳值的設(shè)計不能和表現(xiàn)層環(huán)境綁定,否則就不能復(fù)用了,這也就是為什麼@YaTou 提到了apache-shiro採用的是異常機制處理認證失敗,因為只有這樣才夠通用。
我覺得Controller
不負責(zé)處理數(shù)據(jù)是正確的, 因為在spring-mvc
中Controller
是不能復(fù)用的, 但是如果你把業(yè)務(wù)邏輯抽象成Service
, 那么這個Service
就是可以重複使用的.
至於你說的"在Controller就將物件封裝好了,就免於在Service的方法中多次封裝" , 沒太明白什麼意思, 你每個Service
需要什么參數(shù), Controller
就給什麼參數(shù), 至於需要的參數(shù)是否需要封裝成對象就可以自己權(quán)衡了.
你所說的login(String username,String password)
, 你想抽象成Service
用異常處理處理多種不同的結(jié)果, 這個我覺得完全沒有問題, 而且我覺得非常好啊, 很多認證框架都用的這種方式, 至少我看的apache-shiro
就是用異常處理認證失敗的不同情況的.
第一個問題感覺沒有標準答案,具體情況具體分析,邏輯分層清晰易於維護就好。
第二個問題的話,你這裡給出的交互是隸屬於權(quán)限控制的,一般用filter、aop、代理、反射等等方式實現(xiàn)代碼收束都可以,異常也在這些集中控制的代碼里扔一次就好,直接在Controller裡硬編碼我反而覺得累贅。 Service這一層更多的是呼叫Dao層的方法來實現(xiàn)一些複雜的涉及多表的業(yè)務(wù)邏輯處理,事務(wù)也放在這一層(當(dāng)然現(xiàn)在框架把這事兒都乾了),所以Service這一層一般不丟異常(參數(shù)驗證在Controller以及之前的層次都做掉了因此不出現(xiàn)業(yè)務(wù)相關(guān)異常,而Dao把資料庫相關(guān)的底層異常屏蔽了)。
當(dāng)然這是個人看法,沒有定式,還是那句話,分層清晰易於維護就好。
1、Controller預(yù)設(shè)是單例的,但可用@Scope(value = "prototype")替換
2、登入可以回傳int啊,自己加個枚舉
3、規(guī)定是在Service層處理邏輯,要看業(yè)務(wù)的吧,程式碼冗餘度低些好,也好優(yōu)化
大家觀點會不同,做開發(fā)更多的還是優(yōu)化改,降低冗餘度,而不是必須怎麼做。 。 。
1,Controller應(yīng)盡可能的不設(shè)計業(yè)務(wù)邏輯,只涉及交互
2,Service為可復(fù)用的業(yè)務(wù)邏輯
3,Controller為Service的上級調(diào)用方
4,你這個case可以在Service中返回固定的傳回值,在Controller層做判斷,並拋出你想對應(yīng)的例外。
當(dāng)然了這只是我們目前的做法,分享一下。 。 。
建議邏輯放在service,我們最近在做分散式微服務(wù)架構(gòu),我們之前是放在controller層的,拆分的時候基本上全部重新,另外有些app需要使用的接口如果放在controller就無法共用,蛋疼吧?
封裝物件到底在Controller還是Service,還是要看具體的情況,個人認為如果是簡單的參數(shù)在Controller中進行封裝時是完全可以的,如果放到Service反而會顯得很冗餘,而且導(dǎo)致Service通用性變差;對於第二個問題,不容同意透過枚舉或不同的狀態(tài)碼來在Controller左做判斷拋出異常,完全可以自己定義一套異常處理機制,直接在Service層拋出,項目有針對此類別業(yè)務(wù)異常的處理機制,直接兩將Service的錯誤訊息和錯誤碼回復(fù)到View層,讓客戶端根據(jù)狀態(tài)碼和錯誤訊息作處理