Temiz bir mimari için hizmet ve depo arasında bir katman kullanmalı mıyım - Bahar


9

Bir mimaride çalışıyorum, web istemcisi ve mobil uygulamalar için bir dinlenme API'si sunacak. Spring kullanıyorum (spring mvc, spring data jpa, ... etc). Etki alanı modeli JPA belirtimi ile kodlanmıştır.

Bazı temiz mimari kavramlarını uygulamaya çalışıyorum ( https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html ). Hepsi değil, çünkü jpa etki alanı modelini koruyacağım.

Katmanlar arasındaki gerçek akış şudur:

Kullanıcı arabirimi <--> API Hizmeti -> Hizmet -> Depo -> DB

  • Kullanıcı arabirimi : web istemcisi, mobil uygulamalar
  • API Hizmeti : Dinlenme Denetleyicileri, burada dönüştürücüler, dto ve çağrı hizmetleri kullanıyorum
  • Hizmet : Uygulamalarla arayüzler ve iş mantığı içerir
  • Havuz : CRUD işlemlerini ve belki de bazı sql sorgularını barındıran otomatik uygulamalara sahip (yay verileri jpa tarafından yapılır) havuz arabirimleri

Şüphem: Hizmet ve depo arasında fazladan bir katman kullanmalı mıyım?

Bu yeni akışı planlıyorum:

Kullanıcı arabirimi <--> API Hizmeti -> Hizmet -> Kalıcılık -> Depo -> DB

Bu kalıcılık katmanını neden kullanmalıyım? Temiz mimari makalesinde söylediği gibi, agnostik bir kalıcılık katmanına erişen bir hizmet uygulamasına (iş mantığı veya kullanım durumu) sahip olmak istiyorum. Başka bir "veri erişimi" kalıbı kullanmaya karar verirsem, örneğin depo kullanmayı bırakmaya karar verirsem değişiklik yapmaya gerek kalmaz.

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

Bu yüzden böyle bir kalıcılık katmanı kullanmayı düşünüyorum:

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

ve kalıcılık katmanının şu şekilde uygulanması:

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

Bu yüzden sadece kalıcılık katmanı uygulamalarını değiştirmem gerekiyor, hizmeti değişiklik yapmadan bıraktım. Havuzun çerçeveyle ilgili olması gerçeğiyle çiftleşti.

Ne düşünüyorsun? Teşekkürler.


3
veri havuzu soyutlama katmanıdır. başka bir tane eklemek yardımcı
Ewan

Oh, ama Bahar'ın önerdiği arayüzü kullanmak zorundayım, yani depo yöntemi isimleri. Depoyu değiştirmek istersem, arayan isimlerini saklamam gerekecekti, değil mi?
Alejandro

Bana bahar deposu bahar nesnelerini göstermeye zorlamıyor gibi geliyor. Sadece agnostik bir arayüz uygulamak için kullanın
Ewan

Yanıtlar:


6

Kullanıcı arabirimi <--> API Hizmeti -> Hizmet -> Depo -> DB

Sağ. Bu, Spring Framework tarafından önerilen endişelerin ayrılmasıyla elde edilen temel tasarımdır. Yani " Baharın doğru yolundasınız ".

Depolar sık sık DAO olarak kullanılmasına rağmen , Bahar geliştiricilerinin Depo kavramını Eric Evans'ın DDD'sinden aldıkları gerçeği . Havuz arayüzleri CRUD yöntemleri nedeniyle DAO'lara çok benzeyecek ve birçok geliştirici havuzların arayüzlerini yapmaya çalıştıkları için jenerikler, sonunda EntityManager (burada gerçek DAO) 1 ile desteklenmiyorlar. sorgular ve kriterler.

Yay bileşenlerine çevrilen tasarımınız şuna benzer

@RestController > @Service > @Repository >  EntityManager

Depo, hizmetler ve veri depoları arasında zaten bir soyutlamadır. Spring Data JPA veri havuzu arayüzlerini genişlettiğimizde, bu tasarımı örtük olarak uyguluyoruz. Bunu yaptığımızda bir vergi ödüyoruz: Spring'in bileşenleri ile sıkı bir bağlantı. Ayrıca, ihtiyaç duymayabileceğimiz veya istemeyebileceğimiz birkaç yöntemi miras alarak LoD ve YAGNI'yi kırıyoruz. Böyle bir arayüzün bize sundukları alan adı ihtiyaçları hakkında değerli bir bilgi sağlamadığından bahsetmiyoruz bile.

Bununla birlikte, Bahar Verileri JPA depolarını genişletmek zorunlu değildir ve daha düz ve özel bir sınıf hiyerarşisi uygulayarak bu ödünleşmeden kaçınabilirsiniz.

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

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

        //Interface implentation
        //...
    }

Veri kaynağını değiştirmek artık EntityManager'ı farklı bir veri kaynağıyla değiştiren yeni bir uygulamayı gerektiriyor .

    //@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
        //...
    }

ve bunun gibi. 2

Bir soyutlama katmanı daha eklemeniz gerekip gerekmediği sorusuna geri dönersek, hayır diyebilirim çünkü gerekli değil. Örneğiniz sadece daha fazla karmaşıklık katıyor. Teklif ettiğiniz katman, hizmetler ve havuzlar arasında bir proxy olarak veya belirli bir mantık gerektiğinde ve nereye yerleştireceğinizi yoksa sahte bir hizmet deposu katmanı olarak sonuçlanacaktır .


1: Birçok geliştiricinin düşündüğünün aksine, veri havuzu arabirimleri birbirinden tamamen farklı olabilir, çünkü her veri havuzu farklı etki alanı ihtiyaçlarına hizmet eder. Spring Data JPA'da DAO rolü EntityManager tarafından oynanır . Oturumları, DataSource'a erişimi , eşlemeleri vb. Yönetir .

2: Benzer bir çözüm, Spring'in depo arayüzlerini özel arayüzlerle karıştırarak geliştirmektir. Daha fazla bilgi için BaseRepositoryFactoryBean ve @NoRepositoryBean'a bakın . Ancak, bu yaklaşımı hantal ve kafa karıştırıcı buldum.


3

Bir tasarımın esnek olduğunu kanıtlamanın en iyi yolu, onu esnetmektir.

Kodunuzda kalıcılıktan sorumlu olan ancak bir havuz kullanma fikrine bağlı olmayan bir yer istiyorsunuz. İnce. Şu anda faydalı bir şey yapmıyor ... iç çekiş, tamam.

Tamam, bu şant katmanının iyi olup olmadığını test edelim. Ürünlerinizi dosyalarda saklayacak düz bir dosya katmanı oluşturun. Şimdi bu yeni katman bu tasarımda nereye gidiyor?

Peki DB olduğu yere gitmek mümkün olmalıdır. Ne de olsa artık düz dosyaları kullandığımız için DB'ye ihtiyacımız yok. Ancak bunun da depoya ihtiyaç duymaması gerekiyordu.

Belki de havuzun bir uygulama detayı olduğunu düşünün. Ne de olsa veri tabanı desenini kullanmadan DB ile konuşabilirim.

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

Tüm bunları Servise dokunmadan çalıştırabiliyorsanız esnek kodunuz vardır.

Ama benim sözüme güvenme. Yazın ve ne olduğunu görün. Esnek olmak için güvendiğim tek kod esnek kod.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.