ฉันควรใช้เลเยอร์ระหว่างบริการและพื้นที่เก็บข้อมูลสำหรับสถาปัตยกรรมสะอาด - สปริง


9

ฉันกำลังทำงานในสถาปัตยกรรมมันจะให้ api ที่เหลือสำหรับเว็บไคลเอ็นต์และแอพมือถือ ฉันกำลังใช้สปริง (สปริง mvc, สปริงข้อมูล jpa, ... ฯลฯ ) รูปแบบโดเมนนั้นมีรหัสด้วย JPA

ฉันพยายามที่จะใช้แนวคิดบางอย่างของสถาปัตยกรรมที่สะอาด ( https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html ) ไม่ใช่ทั้งหมดเพราะฉันจะคงรูปแบบโดเมน jpa ไว้

การไหลที่แท้จริงผ่านเลเยอร์คือ:

ส่วนหน้า <--> บริการ API -> บริการ -> พื้นที่เก็บข้อมูล -> ฐานข้อมูล

  • ส่วนหน้า : เว็บไคลเอ็นต์แอปมือถือ
  • บริการ API : Rest Controllers ที่นี่ฉันใช้ตัวแปลงและ dto's และบริการโทร
  • บริการ : การเชื่อมต่อกับการใช้งานและมีตรรกะทางธุรกิจ
  • พื้นที่เก็บข้อมูล : ส่วนต่อประสานที่เก็บข้อมูลที่มีการใช้งานโดยอัตโนมัติ (ทำโดย data ฤดูใบไม้ผลิ jpa) ซึ่งมีการดำเนินการ CRUD และอาจมีการสอบถาม sql บางส่วน

ข้อสงสัยของฉัน: ฉันควรใช้เลเยอร์พิเศษระหว่างบริการและที่เก็บ?

ฉันกำลังวางแผนโฟลว์ใหม่นี้:

ส่วนหน้า <--> บริการ API -> บริการ -> ความคงทน -> พื้นที่เก็บข้อมูล -> ฐานข้อมูล

เหตุใดจึงต้องใช้เลเยอร์การคงอยู่นี้ ตามที่กล่าวไว้ในบทความสถาปัตยกรรมที่สะอาดฉันต้องการมีการใช้บริการ (ตรรกะทางธุรกิจหรือกรณีการใช้งาน) ที่เข้าถึงเลเยอร์คงอยู่ไม่เชื่อเรื่องพระเจ้า และไม่จำเป็นต้องเปลี่ยนแปลงหากฉันตัดสินใจใช้รูปแบบ "data access" อื่นเช่นถ้าฉันตัดสินใจหยุดใช้ที่เก็บ

class ProductServiceImpl implements ProductService {
    ProductRepository productRepository;
    void save(Product product) {
        // do business logic
        productRepository.save(product)
    }
}

ดังนั้นฉันจึงใช้เลเยอร์คงอยู่เช่นนี้:

class ProductServiceImpl implements ProductService {
    ProductPersistence productPersistence;
    void save(Product product) {
        // do business logic
        productPersistence.save(product)
    }
}

และการนำเลเยอร์ติดตามาใช้เช่นนี้

class ProductPersistenceImpl implements ProductPersistence {
    ProductRepository productRepository;
    void save(Product product) {
        productRepository.save(product)
    }
}

ดังนั้นฉันจะต้องเปลี่ยนการใช้งานของชั้นการคงอยู่ออกจากบริการโดยไม่มีการเปลี่ยนแปลงควบคู่ไปกับความจริงที่ว่าพื้นที่เก็บข้อมูลที่เกี่ยวข้องกับกรอบ

คุณคิดอย่างไร? ขอบคุณ


3
พื้นที่เก็บข้อมูลเป็นชั้นที่เป็นนามธรรม เพิ่มอีกหนึ่งไม่ได้ช่วย
Ewan

โอ้ แต่ฉันจะต้องใช้ส่วนต่อประสานที่เสนอโดย Spring ฉันหมายถึงชื่อวิธีการเก็บข้อมูล และถ้าฉันต้องการเปลี่ยนที่เก็บฉันจะต้องเก็บชื่อการโทรไว้ไม่ใช่หรือ?
Alejandro

ดูเหมือนว่าที่เก็บสปริงไม่ได้บังคับให้คุณเปิดเผยวัตถุสปริง เพียงแค่ใช้มันเพื่อใช้อินเทอร์เฟซผู้ไม่เชื่อเรื่องพระเจ้า
Ewan

คำตอบ:


6

ส่วนหน้า <--> บริการ API -> บริการ -> พื้นที่เก็บข้อมูล -> ฐานข้อมูล

ขวา. นี่เป็นการออกแบบขั้นพื้นฐานโดยแยกข้อกังวลที่เสนอโดย Spring Framework ดังนั้นคุณจึงอยู่ใน " ทางที่ถูกต้องของฤดูใบไม้ผลิ "

แม้จะมีการใช้ที่เก็บบ่อยครั้งเป็น DAOs แต่ความจริงก็คือนักพัฒนาของฤดูใบไม้ผลิใช้แนวคิดของRepositoryจาก DDD ของ Eric Evans อินเทอร์เฟซที่เก็บข้อมูลมักจะมีลักษณะคล้ายกับ DAOs มากเนื่องจากวิธีการ CRUD และเนื่องจากนักพัฒนาหลายคนพยายามที่จะสร้างส่วนต่อประสานของที่เก็บข้อมูลดังนั้นข้อมูลทั่วไปในท้ายที่สุดพวกเขาไม่มีความแตกต่างกับEntityManager (DAO จริงที่นี่) 1แต่การสนับสนุน ข้อความค้นหาและเกณฑ์

แปลเป็นส่วนประกอบของสปริงการออกแบบของคุณคล้ายกับ

@RestController > @Service > @Repository >  EntityManager

พื้นที่เก็บข้อมูลเป็นสิ่งที่เป็นนามธรรมอยู่ระหว่างบริการและที่เก็บข้อมูล เมื่อเราขยายส่วนต่อประสานที่เก็บข้อมูล Spring Data JPA เรากำลังนำการออกแบบนี้ไปใช้โดยปริยาย เมื่อเราทำเช่นนี้เรากำลังจ่ายภาษี: การมีเพศสัมพันธ์อย่างแน่นหนากับส่วนประกอบของสปริง นอกจากนี้เราทำลาย LoD และ YAGNI โดยสืบทอดวิธีการต่าง ๆ ที่เราอาจไม่ต้องการหรือไม่ต้องการ ไม่ต้องพูดถึงว่าอินเทอร์เฟซดังกล่าวไม่ได้ให้ข้อมูลเชิงลึกที่มีค่าเกี่ยวกับความต้องการของโดเมนแก่เรา

ที่กล่าวว่าการขยายที่เก็บข้อมูล JPA ของ Spring Data นั้นไม่จำเป็นและคุณสามารถหลีกเลี่ยงการแลกเปลี่ยนเหล่านี้ได้โดยการใช้ลำดับชั้นของคลาสที่เรียบง่ายและกำหนดเองมากขึ้น

    @Repository
    public class MyRepositoryImpl implements MyRepository{
        private EntityManager em;

        @Autowire
        public MyRepository (EntityManager em){    
             this.em = em;
        }

        //Interface implentation
        //...
    }

ตอนนี้การเปลี่ยนแหล่งข้อมูลใช้เวลาเพียงการนำไปใช้งานใหม่ซึ่งแทนที่EntityManagerด้วยแหล่งข้อมูลอื่น

    //@RestController > @Service > @Repository >  RestTemplate

    @Repository
    public class MyRepositoryImpl implements MyRepository{
        private RestTemplate rt;

        @Autowire 
        public MyRepository (RestTemplate rt){    
             this.rt = rt;
        }

        //Interface implentation
        //...
    }
    //@RestController > @Service > @Repository >  File

    @Repository
    public class MyRepositoryImpl implements MyRepository{

        private File file; 
        public MyRepository (File file){    
            this.file = file;
        }

        //Interface implentation
        //...
    }
    //@RestController > @Service > @Repository >  SoapWSClient

    @Repository
    public class MyRepositoryImpl implements MyRepository{

        private MyWebServiceClient wsClient; 

        @Autowire
        public MyRepository (MyWebServiceClient  wsClient){    
               this.wsClient = wsClient;
        }

        //Interface implentation
        //...
    }

และอื่น ๆ 2

กลับไปที่คำถามว่าคุณควรเพิ่มเลเยอร์สิ่งที่เป็นนามธรรมอีกหนึ่งชั้นฉันจะบอกว่าไม่เพราะมันไม่จำเป็น ตัวอย่างของคุณเพิ่มความซับซ้อนมากขึ้นเท่านั้น เลเยอร์ที่คุณเสนอจะสิ้นสุดลงในฐานะพร็อกซีระหว่างบริการและที่เก็บข้อมูลหรือเป็นเลเยอร์ปลอมที่ให้บริการเมื่อต้องการตรรกะเฉพาะและคุณไม่ต้องวาง


1: ซึ่งแตกต่างจากนักพัฒนาหลายคนคิดว่าที่เก็บอินเทอร์เฟซสามารถแตกต่างจากกันโดยสิ้นเชิงเพราะแต่ละพื้นที่เก็บข้อมูลตอบสนองความต้องการโดเมนที่แตกต่างกัน ในฤดูใบไม้ผลิข้อมูล JPA บทบาทDAOมีการเล่นโดยEntityManager จะจัดการประชุมการเข้าถึงไปยังแหล่งข้อมูล , การแมปฯลฯ

2: โซลูชันที่คล้ายกันคือการปรับปรุงอินเตอร์เฟสของที่เก็บของสปริงให้ผสมกับอินเตอร์เฟสแบบกำหนดเอง สำหรับข้อมูลเพิ่มเติมให้มองหา BaseRepositoryFactoryBeanและ@NoRepositoryBean อย่างไรก็ตามฉันได้พบวิธีการนี้ยุ่งยากและสับสน


3

วิธีที่ดีที่สุดในการพิสูจน์ว่าการออกแบบมีความยืดหยุ่นคือการงอ

คุณต้องการสถานที่ในรหัสของคุณที่รับผิดชอบการติดตา แต่ไม่เชื่อมโยงกับแนวคิดของการใช้ที่เก็บ ละเอียด. มันไม่ได้ทำอะไรที่มีประโยชน์ในขณะนี้ ... ถอนหายใจได้ดี

ตกลงให้ทดสอบว่าเลเยอร์ปัดนี้ทำได้ดีหรือไม่ สร้างเลเยอร์ไฟล์เรียบที่จะคงอยู่กับผลิตภัณฑ์ของคุณในไฟล์ เลเยอร์ใหม่นี้จะไปไหนในการออกแบบนี้

มันควรจะไปที่ฐานข้อมูลได้ดี ท้ายที่สุดเราไม่ต้องการฐานข้อมูลอีกต่อไปเนื่องจากเราใช้ไฟล์แฟลต แต่นี่ก็ควรที่จะไม่ต้องการพื้นที่เก็บข้อมูล

พิจารณาว่าที่เก็บอาจเป็นรายละเอียดการใช้งาน หลังจากทั้งหมดฉันสามารถพูดคุยกับฐานข้อมูลโดยไม่ต้องใช้รูปแบบพื้นที่เก็บข้อมูล

Front end <--> API Service -> Service -> Repository -> DB

Front end <--> API Service -> Service -> Repository -> Files

Front end <--> API Service -> Service -> Persistence -> DB

Front end <--> API Service -> Service -> Persistence -> Files

หากคุณสามารถใช้งานได้ทั้งหมดโดยไม่ต้องแตะที่ Service คุณจะได้รหัสที่ยืดหยุ่นได้

แต่อย่าใช้คำของฉันมัน เขียนและดูว่าเกิดอะไรขึ้น รหัสเดียวที่ฉันเชื่อว่ามีความยืดหยุ่นคือรหัสยืดหยุ่น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.