我应该在服务和存储库之间使用一个层来构建干净的架构-Spring


9

我正在架构中,它将为Web客户端和移动应用程序提供rest api。我正在使用Spring(spring mvc,spring data jpa,... etc)。域模型使用JPA规范编码。

我正在尝试应用干净架构的一些概念(https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html)。并非全部,因为我将保留jpa域模型。

通过各层的实际流量是这样的:

前端 <-> API服务 -> 服务 -> 存储库 -> 数据库

  • 前端:Web客户端,移动应用
  • API服务:Rest控制器,在这里我使用转换器和dto并调用服务
  • 服务:与实现的接口,并且包含业务逻辑
  • 存储库:具有自动实现的存储库接口(由spring data jpa完成),它包含CRUD操作以及一些sql查询

我的疑问:我应该在服务和存储库之间使用额外的一层吗?

我正在计划这个新流程:

前端 <-> API服务 -> 服务 -> 持久性 -> 存储库 -> 数据库

为什么要使用这个持久层?正如它在干净的体系结构文章中所说的,我希望有一个访问不可知的持久层的服务实现(业务逻辑或用例)。如果我决定使用其他“数据访问”模式,例如,如果我决定停止使用存储库,则不需要更改。

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建议的接口,即存储库方法名称。如果要更改存储库,则必须保留调用名称,不是吗?
亚历杭德罗

在我看来,Spring存储库不会强迫您公开Spring对象。只需使用它即可实现不可知的界面
Ewan

Answers:


6

前端<-> API服务->服务->存储库->数据库

对。这是Spring Framework提出的基于关注点分离的基本设计。因此,您处于“ 春天的正确道路 ”上。

尽管Repository经常被用作DAO,但事实是Spring开发人员从Eric Evans的DDD中采用了Repository的概念。库接口往往会看起来非常相似,由于CRUD方法的DAO和因为许多开发商努力使资源库的接口,这样,最终,他们与无差异仿制药的EntityManager(真在这里DAO)1但支持查询和条件。

转换为Spring组件后,您的设计类似于

@RestController > @Service > @Repository >  EntityManager

存储库已经是服务和数据存储之间的抽象。扩展Spring Data JPA存储库接口时,我们将隐式实现此设计。当我们这样做时,我们要缴税:与Spring的组件紧密耦合。此外,我们通过继承一些我们可能不需要或不希望使用的方法来破坏LoD和YAGNI。更不用说这样的界面不能为我们提供有关它们所服务的领域需求的任何有价值的见解。

就是说,扩展Spring Data JPA存储库不是强制性的,您可以通过实现更简单和自定义的类层次结构来避免这些折衷。

    @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:与许多开发人员不同,存储库接口可以彼此完全不同,因为每个存储库都满足不同的域需求。在Spring Data JPA中,角色DAOEntityManager扮演。它管理会话,对DataSource的访问,映射等。

2:类似的解决方案是增强Spring的存储库接口,将它们与自定义接口混合在一起。有关更多信息,请查找 BaseRepositoryFactoryBean@NoRepositoryBean。但是,我发现这种方法既麻烦又令人困惑。


3

证明设计灵活的最好方法是对其进行伸缩。

您需要在代码中保留一个负责持久性但又不依赖于使用存储库的想法的位置。精细。目前,它没有做任何有用的事情。

好,让我们测试一下该分流层是否有任何好处。创建一个平面文件层,将您的产品保留在文件中。现在,这个新层在设计中要放在哪里?

好吧,它应该能够到达数据库所在的位置。毕竟,由于我们使用的是平面文件,因此不再需要DB。但这也应该不需要存储库。

考虑一下,也许存储库是一个实现细节。毕竟,我无需使用存储库模式就可以与数据库对话。

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

如果您无需触摸服务即可完成所有工作,那么您将获得灵活的代码。

但是不要相信我。写下来,看看会发生什么。我相信唯一灵活的代码就是弹性代码。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.