国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

領域/驅動設計對軟件復雜度的應對

開發 開發工具
領域驅動設計關注的焦點在于領域和領域邏輯,因為軟件系統的本質其實是給客戶(用戶)提供具有業務價值的領域功能。

不管是因為規模與結構制造的理解力障礙,還是因為變化帶來的預測能力問題,最終的決定因素還是因為需求。Eric Evans認為“很多應用程序最主要的復雜性并不在技術上,而是來自領域本身、用戶的活動或業務”。因而,領域驅動設計關注的焦點在于領域和領域邏輯,因為軟件系統的本質其實是給客戶(用戶)提供具有業務價值的領域功能。

軟件復雜度

一、需求引起的軟件復雜度

需求分為業務需求與質量屬性需求,因而需求引起的復雜度可以分為兩個方面:技術復雜度與業務復雜度。

技術復雜度來自需求的質量屬性,諸如安全、高性能、高并發、高可用性等需求,為軟件設計帶來了極大的挑戰。讓人難受的是這些因素彼此之間又可能互相矛盾互相影響。例如,系統安全性要求對訪問進行控制,無論是增加防火墻,還是對傳遞的消息進行加密,又或者對訪問請求進行認證和授權,都需要為整個系統架構添加額外的間接層。這不可避免會對訪問的低延遲產生影響,拖慢了系統的整體性能。又例如為了滿足系統的高并發訪問,我們需要對應用服務進行物理分解,通過橫向增加更多的機器來分散訪問負載;同時,我們還可以將一個同步的訪問請求拆分為多級步驟的異步請求,再通過引入消息中間件對這些請求進行整合和分散處理。這種分離一方面增加了系統架構的復雜性,另一方面也因為引入了更多的資源,使得系統的高可用面臨挑戰,并增加了維護數據一致性的難度。

業務復雜度對應了客戶的業務需求,因而這種復雜度往往會隨著需求規模的增大而增加。由于需求不可能做到完全獨立,一旦規模擴大到一定程度,不僅產生了功能數量的增加,還會因為功能互相之間的依賴與影響使得這種復雜度產生疊加,進而影響到整個系統的質量屬性,例如系統的可維護性與可擴展性。在考慮系統的業務需求時,還會因為溝通不暢、客戶需求不清晰等多種局外因素帶來需求的變更和修改。如果不能很好地控制這種變更,就可能因為多次修改導致業務邏輯糾纏不清,系統可能開始慢慢腐爛,變得不可維護,最終形成一種如Brian Foote和Joseph Yoder所說的“大泥球”系統。

以電商系統的促銷規則為例。針對不同類型的顧客與產品,商家會提供不同的促銷力度;促銷的形式多種多樣,包括贈送積分、紅包、優惠券、禮品;促銷的周期需要支持定制,既可以是特定的日期,例如雙十一促銷,也可以是節假日的固定促銷模式。如果我們在設計時沒有充分考慮促銷規則的復雜度,并處理好促銷規則與商品、顧客、賣家與支付乃至于物流、倉儲之間的關系,開發過程就會變得踉踉蹌蹌,舉步維艱。

技術復雜度與業務復雜度并非完全獨立,二者混合在一起產生的化合作用更讓系統的復雜度變得不可預期,難以掌控。同時,技術的變化維度與業務的變化維度并不相同,產生變化的原因也不一致,倘若未能很好地界定二者之間的關系,系統架構缺乏清晰邊界,會變得難以梳理。復雜度一旦增加,團隊規模也將隨之擴大,再揉以嚴峻的交付周期、人員流動等諸多因素,就好似將各種不穩定的易燃易爆氣體混合在一個不可逃逸的密閉容器中一般,隨時都可能爆炸:

軟件復雜度

隨著業務需求的增加與變化,以及對質量屬性的高標準要求,自然也引起了軟件系統規模的增大與結構的繁雜,至于變化,則是軟件開發繞不開的話題。因此,當我們面對一個相對復雜的軟件系統時,通常面臨的問題在于:

  • 問題域過于龐大而復雜,使得從問題域中尋求解決方案的挑戰增加。該問題與軟件系統的規模有關。
  • 開發人員將業務邏輯的復雜度與技術實現的復雜度混淆在一起。該問題與軟件系統的結構有關。
  • 隨著需求的增長和變化,無法控制業務復雜度和技術復雜度。該問題與軟件系統的變化有關。

針對這三個問題,領域驅動設計都給出了自己的應對措施。

二、領域驅動設計的應對措施

1. 隔離業務復雜度與技術復雜度

要避免業務邏輯的復雜度與技術實現的復雜度混淆在一起,首要任務就是確定業務邏輯與技術實現的邊界,從而隔離各自的復雜度。這種隔離也是題中應有之義,畢竟技術與業務的關注點完全不同。例如在電商的領域邏輯中,訂單業務關注的業務規則包括驗證訂單有效性,計算訂單總額,提交和審核訂單的流程等;技術關注點則從實現層面保障這些業務能夠正確地完成,包括確保分布式系統之間的數據一致性,確保服務之間通信的正確性等。

業務邏輯并不關心技術是如何實現的。無論采用何種技術,只要業務需求不變,業務規則就不會變化。換言之,理想狀態下,我們應該保證業務規則與技術實現是正交的。

領域驅動設計通過分層架構與六邊形架構確保業務邏輯與技術實現的隔離。

(1) 分層架構的關注點分離

分層架構遵循了“關注點分離”原則,將屬于業務邏輯的關注點放到領域層(Domain Layer)中,而將支撐業務邏輯的技術實現放到基礎設施層(Infrastructure Layer)中。同時,領域驅動設計又頗具創見的引入了應用層(Application Layer)。應用層扮演了雙重角色。一方面它作為業務邏輯的外觀(Facade),暴露了能夠體現業務用例的應用服務接口;另一方面它又是業務邏輯與技術實現的粘合劑,實現二者之間的協作。

下圖展現的就是一個典型的領域驅動設計分層架構。藍色區域的內容與業務邏輯有關,灰色區域的內容與技術實現有關,二者涇渭分明,然后匯合在應用層。應用層確定了業務邏輯與技術實現的邊界,通過直接依賴或者依賴注入(DI,Dependency Injection)的方式將二者結合起來:

(2) 六邊形架構的內外分離

由Cockburn提出的六邊形架構則以“內外分離”的方式,更加清晰地勾勒出業務邏輯與技術實現的邊界,且將業務邏輯放在了架構的核心位置。這種架構模式改變了我們觀察系統架構的視角:

體現業務邏輯的應用層與領域層處于六邊形架構的內核,并通過內部的六邊形邊界與基礎設施的模塊隔離開。當我們在進行軟件開發時,只要恪守架構上的六邊形邊界,就不會讓技術實現的復雜度污染到業務邏輯,保證了領域的整潔。邊界還隔離了變化產生的影響。如果我們在領域層或應用層抽象了技術實現的接口,再通過依賴注入將控制的方向倒轉,業務內核就會變得更加的穩定,不會因為技術選型或其他決策的變化而導致領域代碼的修改。

(3) 案例:隔離數據庫與緩存的訪問

領域驅動設計建議我們在領域層建立資源庫(Repository)的抽象,它的實現則被放在基礎設施層,然后采用依賴注入在運行時為業務邏輯注入具體的資源庫實現。那么,對于處于內核之外的Repositories模塊而言,即使選擇從MyBatis遷移到Sprint Data,領域代碼都不會受到牽連:

  1. package practiceddd.ecommerce.ordercontext.application; 
  2.  
  3. @Transaction 
  4. public class OrderAppService { 
  5.    @Service 
  6.    private PlaceOrderService placeOrder; 
  7.     
  8.    public void placeOrder(Identity buyerId, List<OrderItem> items, ShippingAddress shipping, BillingAddress billing) { 
  9.        try { 
  10.            palceOrder.execute(buyerId, items, shipping, billing); 
  11.        } catch (OrderRepositoryException | InvalidOrderException | Exception ex) { 
  12.            ex.printStackTrace(); 
  13.            logger.error(ex.getMessage()); 
  14.        } 
  15.    } 
  16.  
  17. package practiceddd.ecommerce.ordercontext.domain; 
  18.  
  19. public interface OrderRepository { 
  20.    List<Order> forBuyerId(Identity buyerId); 
  21.    void add(Order order); 
  22. }  
  23.  
  24. public class PlaceOrderService { 
  25.    @Repository 
  26.    private OrderRepository orderRepository; 
  27.  
  28.    @Service 
  29.    private OrderValidator orderValidator;     
  30.  
  31.    public void execute(Identity buyerId, List<OrderItem> items, ShippingAddress shipping, BillingAddress billing) { 
  32.        Order order = Order.create(buyerId, items, shipping, billing); 
  33.        if (orderValidator.isValid(order)) { 
  34.            orderRepository.add(order); 
  35.        } else { 
  36.            throw new InvalidOrderException(String.format("the order which placed by buyer with %s is invalid.", buyerId)); 
  37.        } 
  38.    } 
  39.  
  40. package practiceddd.ecommerce.ordercontext.infrastructure.db; 
  41.  
  42. public class OrderMybatisRepository implements OrderRepository {} 
  43. public class OrderSprintDataRepository implements OrderRepository {} 

對緩存的處理可以如法炮制,但它與資源庫稍有不同之處。資源庫作為訪問領域模型對象的入口,其本身提供的增刪改查功能,在抽象層面上是對領域資源的訪問。因此在領域驅動設計中,我們通常將資源庫的抽象歸屬到領域層。對緩存的訪問則不相同,它的邏輯就是對key和value的操作,與具體的領域無關。倘若要為緩存的訪問方法定義抽象接口,在分層的歸屬上應該屬于應用層,至于實現則屬于技術范疇,應該放在基礎設施層:

  1. package practiceddd.ecommerce.ordercontext.application; 
  2.  
  3. @Transaction 
  4. public class OrderAppService { 
  5.    @Repository 
  6.    private OrderRepository orderRepository; 
  7.  
  8.    @Service 
  9.    private CacheClient<List<Order>> cacheClient; 
  10.     
  11.    public List<Order> findBy(Identity buyerId) { 
  12.        Optional<List<Order>> cachedOrders = cacheClient.get(buyerId.value()); 
  13.        if (cachedOrders.isPresent()) { 
  14.            return orders.get(); 
  15.        }  
  16.        List<Order> orders = orderRepository.forBuyerId(buyerId); 
  17.        if (!orders.isEmpty()) { 
  18.            cacheClient.put(buyerId.value(), orders); 
  19.        } 
  20.        return orders; 
  21.    } 
  22.  
  23. package practiceddd.ecommerce.ordercontext.application.cache; 
  24.  
  25. public interface CacheClient<T> { 
  26.    Optional<T> get(String key); 
  27.    void put(String key, T value); 
  28.  
  29. package practiceddd.ecommerce.ordercontext.infrastructure.cache; 
  30.  
  31. public class RedisCacheClient<T> implements CacheClient<T> {} 

2. 限界上下文的分而治之

在前面分析緩存訪問接口的歸屬時,我們將接口放在了系統的應用層。從層次的職責來看,這樣的設計是合理的,但它卻使得系統的應用層變得更加臃腫,職責也變得不夠單一了。這是分層架構與六邊形架構的局限所在,因為這兩種架構模式僅僅體現了一個軟件系統的邏輯劃分。倘若我們將一個軟件系統視為一個縱橫交錯的魔方,前述的邏輯劃分僅僅是一種水平方向的劃分。至于垂直方向的劃分,則是面向垂直業務的切割。這種方式更利于控制軟件系統的規模,將一個龐大的軟件系統劃分為松散耦合的多個小系統的組合。

針對前述案例,我們可以將緩存視為一個獨立的子系統。它同樣擁有自己的業務邏輯和技術實現,因而也可以為其建立屬于緩存領域的分層架構。在架構的宏觀視角,這個緩存子系統與訂單子系統處于同一個抽象層次,這一概念在領域驅動設計中,被稱之為限界上下文(Bounded Context)。

針對龐大而復雜的問題域,限界上下文采用了“分而治之”的思想對問題域進行了分解,有效地控制了問題域的規模,進而控制了整個系統的規模。一旦規模減小,無論業務復雜度還是技術復雜度,都會得到顯著的降低,在對領域進行分析以及建模時,也能變得更容易。如果說分層架構與六邊形架構確保了業務邏輯與技術實現的隔離,則限界上下文對整個系統進行了劃分,將一個大系統拆分為一個個小系統后,我們再利用分層架構與六邊形架構思想對其進行邏輯分層,設計會變得更易于把控,系統的架構也會變得更加的清晰。

案例:限界上下文幫助架構的演進

國際報稅系統是為跨國公司的駐外出差雇員(系統中被稱之為Assignee)提供方便一體化的稅收信息填報平臺。客戶是一家會計師事務所,該事務所的專員(Admin)通過該平臺可以收集雇員提交的報稅信息,然后對這些信息進行稅務評審。如果Admin評審出信息有問題,則返回給Assignee重新修改和填報。一旦信息確認無誤,則進行稅收分析和計算,并獲得最終的稅務報告提交給當地政府以及雇員本人。

系統主要涉及的功能包括:

  • 駐外出差雇員的薪酬與福利
  • 稅收計劃與合規評審
  • 對稅收評審的分配管理
  • 稅收策略設計與評審
  • 對駐外出差雇員的稅收合規評審
  • 全球的Visa服務

主要涉及的用戶角色包括:

  • Assignee:駐外出差雇員
  • Admin:稅務專員
  • Client:出差雇員的雇主

在早期的架構設計時,架構師并沒有對整個系統的問題域進行拆分,而是基于用戶角色對系統進行了簡單粗暴的劃分,分為兩個相對獨立的子系統:Frond End與Office End。這兩個子系統單獨部署,分別面向Assignee與Admin。系統之間的集成則通過消息和Web Service進行通信。兩個子系統的開發分屬不同的團隊,Frond End由美國的團隊負責開發與維護,而Office End則由印度的團隊負責。整個架構如下圖所示:

采用這種架構面臨如下問題:

  • 龐大的代碼庫:整個Front End和Office End都沒有做物理分解,隨著需求的增多,代碼庫變得格外龐大
  • 分散的邏輯:系統分解的邊界是不合理的,沒有按照業務分解,而是按照用戶的角色進行分解,導致大量相似的邏輯分散在兩個不同的子系統中
  • 重復的數據:兩個子系統中存在業務重疊,因而也導致了部分數據的重復
  • 復雜的集成:Front End與Office End因為某些相關的業務需要彼此通信,這種集成關系是雙向的,且由兩個不同的團隊開發,導致集成的接口混亂,消息協議多樣化
  • 知識未形成共享:兩個團隊完全獨立開發,沒有掌握端對端的整體流程,團隊之間沒有形成知識的共享
  • 無法應對需求變化: 新增需求包括對國際旅游、Visa的支持,現有系統的架構無法很好地支持這些變化

采用領域驅動設計,我們將架構的主要關注點放在了“領域”,與客戶進行了充分的需求溝通和交流。通過分析已有系統的問題域,結合客戶提出的新需求,對整個問題域進行了梳理,并利用限界上下文對問題域進行了分解,獲得了如下限界上下文:

  • Account Management:管理用戶的身份與配置信息
  • Calendar Management:管理用戶的日程與旅行足跡

之后,客戶希望能改進需求,做到全球范圍內的工作指派與管理,目的在于提高公司的運營效率。通過對領域的分析,我們又識別出兩個限界上下文。在原有的系統架構中,這兩個限界上下文同時處于Front End與Office End之中,屬于重復開發的業務邏輯:

  • Work Record Management:實現工作的分配與任務的跟蹤
  • File Sharing:目的是實現客戶與會計師事務所之間的文件交換

隨著我們對領域知識的逐漸深入理解與分析,又隨之識別出如下限界上下文:

  • Consent:管理合法的遵守法規的狀態
  • Notification:管理系統與客戶之間的交流
  • Questionnaire:對問卷調查的數據收集

這個領域分析的過程實際上就是通過對領域的分析,引入限界上下文對問題域進行分解,通過降低規模的方式降低問題域的復雜度;同時,通過為模型確定清晰的邊界,使得系統的結構變得更加的清晰,保證了領域邏輯的一致性。一旦確定了清晰的領域模型,就能夠幫助我們更加容易地發現系統的可重用點與可擴展點,并遵循“高內聚松耦合”原則對系統職責進行合理分配,再輔以分層架構劃分邏輯邊界,如下圖所示:

我們將識別出來的限界上下文定義為微服務,并對外公開REST服務接口。UI Applications是一個薄薄的展現層,它會調用后端的RESTful服務,也使得服務在保證接口不變的前提下能夠單獨演化。每個服務都是獨立的,可以單獨部署,因而可以針對服務建立單獨的代碼庫和對應的特性團隊(Feature Team)。服務的重用性和可擴展性也有了更好的保障,服務與UI之間的集成變得更簡單,整個架構更加清晰了。

3. 領域模型對領域知識的抽象

領域模型是對業務需求的一種抽象,表達了領域概念、領域規則以及領域概念之間的關系。一個好的領域模型是對統一語言的可視化表示,通過它可以減少需求溝通可能出現的歧義;通過提煉領域知識,并運用抽象的領域模型去表達,就可以達到對領域邏輯的化繁為簡。模型是封裝,實現了對業務細節的隱藏;模型是抽象,提取了領域知識的共同特征,保留了面對變化時能夠良好擴展的可能性。

案例:項目管理系統的領域模型

我們開發的項目管理系統需要支持多種軟件項目管理流程,例如瀑布、RUP、XP或者Scrum。這些項目管理流程是迥然不同的,如果需要各自提供不同的解決方案,就會使得系統的模型變得非常復雜,也可能引入許多不必要的重復。通過領域建模,我們可以對項目管理領域的知識進行抽象,尋找具有共同特征的領域概念。這就需要分析各種項目管理流程的主要特征與表現,才能從中提煉出領域模型。

瀑布式軟件開發由需求、分析、設計、編碼、測試、驗收六個階段構成,每個階段都由不同的活動構成,這些活動可能是設計或開發任務,也可能是召開評審會。流程如下圖所示:

RUP清晰地劃分了四個階段:先啟階段、細化階段、構造階段與交付階段。每個階段可以包含一到多個迭代,每個迭代有不同的工作,例如業務建模、分析設計、配置與變更管理等。RUP的流程如下圖所示:

XP作為一種敏捷方法,采用了迭代的增量式開發,提倡為客戶交付具有業務價值的可運行軟件。在執行交付計劃之前,XP要求團隊對系統的架構做一次預研(Architectual Spike,又被譯為架構穿刺)。當架構的初始方案確定后,就可以進入每次小版本的交付。每個小版本交付又被劃分為多個周期相同的迭代。在迭代過程中,要求執行一些必須的活動,如編寫用戶故事、故事點估算、驗收測試等。XP的流程如下圖所示:

Scrum同樣是迭代的增量開發過程。項目在開始之初,需要在準備階段確定系統愿景、梳理業務用例、確定產品待辦項(product backlog)、制定發布計劃以及組建團隊。一旦在確定了產品待辦項以及發布計劃之后,就進入sprint迭代階段。sprint迭代過程是一個固定時長的項目過程,在這個過程中,整個團隊需要召開計劃會議、每日站會、評審會議和回顧會議。Scrum的流程如下圖所示:

不同的項目管理流程具有不同的業務概念。例如瀑布式開發分為了六個階段,但卻沒有發布和迭代的概念。RUP沒有發布的概念,而Scrum又為迭代引入了sprint的概念。

不同的項目管理流程具有不同的業務規則。例如RUP的四個階段會包含多個迭代周期,每個迭代周期都需要完成對應的工作,只是不同的工作在不同階段所占的比重不同。XP需要在進入發布階段之前,進行架構預研,而在每次小版本發布之前,都需要進行驗收測試和客戶驗收。Scrum的sprint是一個基本固定的流程,每個迭代召開的四會(計劃會議、評審會議、回顧會議與每日站會)都有明確的目標。

領域建模就是要從這些紛繁復雜的領域邏輯中尋找到能夠表示項目管理領域的概念,并利用面向對象建模范式或其他范式對概念進行抽象,并確定它們之間的關系。經過對這些項目管理流程的分析,我們雖然發現在業務概念和規則上確有不同之處,但由于它們都歸屬于軟件開發領域,我們自然也能尋找到某些共同特征的蛛絲馬跡。

首先,從項目管理系統的角度看,無論針對何種項目管理流程,我們的主題需求是不變的,就是要為這些管理流程制定軟件開發計劃(Plan)。不同之處在于,計劃可以由多個階段(Phase)組成,也可以由多個發布(Release)組成。一些項目管理流程沒有發布的概念,我們可以認為是一個發布。那么,到底是發布包含了多個階段,還是階段包含了多個發布呢?我們發現在XP中,明顯地劃分了兩個階段:Architecture Spike與Release Planning,而發布只屬于Release Planning階段。因而從概念內涵上,我們可以認為是階段(Phase)包含了發布(Release)。每個發布又包含了一到多個迭代(Iteration),至于Scrum的sprint概念其實可以看做是迭代的一種特例。每個迭代可以開展多種不同的活動(Activity),這些活動可以是整個團隊參與的會議,也可以是部分成員或特定角色執行的實踐。對于計劃而言,我們還需要跟蹤任務(Task)。與活動不同,任務具有明確的計劃起止時間、實際起止時間、工作量、優先級與承擔人。

于是,我們提煉出如下的統一領域模型:

為了項目管理者更加方便地制定項目計劃,產品經理提出了計劃模板功能。當管理者選擇對應的項目管理生命周期類型后,系統會自動創建滿足其規則的初始計劃。基于該需求,我們更新了之前的領域模型:

在增加的領域模型中,LifeCycleSpecification是一個隱含的概念,遵循領域驅動設計提出的規格(Specification)模式,封裝了項目開發生命周期的約束規則。

領域模型以可視化的方式清晰地表達了業務含義,我們可以根據這個模型來指導后面的程序設計與編碼實現。當增加新的需求或者需求發生變化時,我們能夠敏銳地捕捉到現有模型的不匹配之處,并對其進行更新。領域模型傳遞了知識,可以作為交流的載體,符合人們的心智模型,有利于讓開發人員從紛繁復雜的業務中解脫出來。這是領域驅動設計對于前述第三個問題——控制業務復雜度的解答。

【本文為51CTO專欄作者“張逸”原創稿件,轉載請聯系原作者】

戳這里,看該作者更多好文

責任編輯:趙寧寧 來源: 51CTO專欄
相關推薦

2018-12-18 10:11:37

軟件復雜度軟件系統軟件開發

2024-04-25 08:33:25

算法時間復雜度空間復雜度

2021-09-17 10:44:50

算法復雜度空間

2024-04-16 08:19:40

架構高可用消息隊列

2015-10-13 09:43:43

復雜度核心

2020-12-30 09:20:27

代碼

2017-12-02 18:53:27

滴滴出行業務中臺對策

2020-08-24 08:15:29

軟件互聯網分布式

2021-01-05 10:41:42

算法時間空間

2014-09-11 15:05:40

驅動設計驅動開發

2017-07-14 10:55:05

2009-07-09 10:45:16

C#基本概念復雜度遞歸與接口

2019-11-18 12:41:35

算法Python計算復雜性理論

2014-09-26 10:00:25

驅動設計DDD領域

2021-10-15 09:43:12

希爾排序復雜度

2022-08-16 09:04:23

代碼圈圈復雜度節點

2019-12-24 09:46:00

Linux設置密碼

2022-02-22 10:11:01

系統軟件架構

2020-02-06 13:59:48

javascript算法復雜度

2012-06-21 09:24:05

軟件定義網絡SDN
點贊
收藏

51CTO技術棧公眾號

精品国产一区二区三区久久狼5月| www.成人在线.com| 国产精品一区hongkong| 成人在线观看免费播放| www.亚洲色图.com| 欧美性色黄大片| 国产精品福利视频| 日韩成人精品一区| 欧美一级电影久久| 国产精品嫩模av在线| 国产97在线视频| 久久精品国产大片免费观看| 亚洲自拍小视频免费观看| 欧美激情四色| 欧洲久久久久久| 精品一区二区三区日韩| 成人免费性视频| 国产日产欧美一区| 国产男女爽爽爽| 日韩欧美一区视频| 羞羞视频在线观看免费| 一本大道久久加勒比香蕉| 美国十次综合久久| 国产成人精品免费久久久久| 欧美三级免费| 看一级黄色录像| 国产精品久久综合| 污视频在线看操| 亚洲第一天堂无码专区| 国产成人精品在线观看| 午夜欧美大片免费观看| 国产成人调教视频在线观看| 91亚洲va在线va天堂va国 | 先锋影音日韩| 久久午夜羞羞影院免费观看| 97在线资源| 亚洲国产天堂久久国产91| 91嫩草精品| 国产成人免费观看| 国产一区二三区| 2018高清国产日本一道国产| 欧美日韩国产色站一区二区三区| 电影亚洲一区| 91免费观看网站| 成人综合婷婷国产精品久久蜜臀| 在线小视频网址| 亚洲精品福利免费在线观看| 国产精品一区二区中文字幕 | 欧美丰满熟妇bbbbbb百度| 日韩一区日韩二区| av中文字幕在线播放| 欧美高清videos高潮hd| 亚洲一级黄色| 亚洲综合在线网站| 日韩精品专区在线影院观看| 亚洲精品国产setv| 亚洲精品日韩在线观看| 亚洲午夜电影网| 国产经典一区| 九九99久久| 一区二区三区四区精品在线视频| 爱啪视频在线观看视频免费| 国产精品亚发布| 99精品久久99久久久久| 日本高清中文字幕在线| 2020久久国产精品| 国产福利一区在线| www.中文字幕久久久| 韩国精品美女www爽爽爽视频| 久久亚洲二区| 国产美女视频黄a视频免费| 亚洲第一区第一页| 五月久久久综合一区二区小说| 国产资源在线视频| 日韩免费高清av| 一区二区三区国产精华| 成人淫片免费视频95视频| 亚洲欧美制服第一页| 亚洲一区国产一区| 在线播放av更多| 欧美国产一区二区三区| 国产一区二区三区av电影| 91ph在线| av在线播放网| 在线精品国精品国产尤物884a| 午夜精品视频在线| 欧美变态口味重另类| 韩国中文免费在线视频| 日韩一区二区三区四区五区六区| 国产综合精品一区| 成人夜色视频网站在线观看| 岛国在线视频免费看| 国语自产偷拍精品视频偷 | 中文字字幕在线中文乱码电影| 亚洲天堂免费观看| 一区二区三区高清视频在线观看| 免费视频二区| 91国产精品视频在线| 91社区在线播放| 99久久综合国产精品二区| 亚洲成人自拍| 欧美一区2区视频在线观看| 国模吧视频一区| av成人动漫| 欧美性在线视频| 国产欧美一区二区在线观看| 日韩久久99| av动漫在线看| 久久久精品日本| 成人午夜在线免费| 日本精品在线一区| 二级片在线观看| 亚洲国产精品va在线观看黑人| 日韩天天综合| 成人在线播放视频| 高清日韩一区| 欧美亚洲国产bt| 狠狠入ady亚洲精品| 国际av在线| 久久精品二区| 精品久久国产97色综合| 精品一区二区综合| 成人性生活视频| 国产中文字幕在线免费观看| 久久国产精品影视| 国产精品久久久久久久久动漫| 成人影院中文字幕| 啦啦啦中文高清在线视频 | 色综合久久久久综合99| 国产高清一区| av中文天堂在线| 日韩视频精品| 亚洲天堂免费在线| 国产日韩欧美精品电影三级在线| 欧美午夜在线播放| 激情四房婷婷| 国产精品一区二| 欧美大胆一级视频| 成人一二三区视频| 理论片一区二区在线| 天堂av中文在线资源库| 久久久精品有限公司| 日韩精品在线视频观看| 99久久国产综合精品女不卡| 久久精品福利| 精品无人乱码| 五月天亚洲综合| 久热在线中文字幕色999舞| 亚洲欧美视频在线观看视频| 牛牛国产精品| f2c人成在线观看免费视频| 亚洲熟妇av日韩熟妇在线| 欧美一区二区三区免费观看| 一本一道波多野结衣一区二区| 日韩和欧美一区二区| 精品自拍视频| 导航福利在线| 四虎影院一区二区三区| 久久久精品免费视频| 婷婷中文字幕综合| 久久精品国产第一区二区三区| 97品白浆高清久久久久久| 可以直接在线观看的av| 91九色国产ts另类人妖| 欧美激情视频在线免费观看 欧美视频免费一| 亚洲免费毛片网站| 日韩影院在线观看| 国产精品玖玖玖在线资源| 成人高清网站| 俄罗斯av网站| 国产精品传媒毛片三区| 日韩视频一区在线| 91黄色免费版| 91网站最新网址| 91精品秘密在线观看| 丝袜老师在线| 美女网站在线观看| 青青草视频国产| 成人网页在线免费观看| 国产丝袜一区二区三区| 综合久久久久久| 久久国产生活片100| 精品久久综合| 日韩成人影音| 国产中文字幕在线观看| 虎白女粉嫩尤物福利视频| 精品在线不卡| 欧美性视频网站| 亚洲性av在线| 欧美中文一区二区三区| 国产三级一区二区| 奇米色一区二区三区四区| 国产成人一区| 91久久久久久白丝白浆欲热蜜臀| 国产区在线视频| 亚洲欧美在线精品| 在线播放 亚洲| 国产精品国产亚洲精品看不卡15| 欧美国产中文字幕| 亚洲精品动漫久久久久|