---
title: "Remove Circular Dependencies by Using Dependency Injection and the Repository Pattern in Ruby"
description: "An internal Ruby pricing gem used by Shopify Core and Storefront Renderer needed consumer-owned data while retaining shared pricing knowledge, creating a circular dependency. The proposed design puts calculation logic and shared domain types in a stateless gem, while each consumer implements a repository contract for retrieving and returning the required data. Constructor injection passes a PricingRepositoryInterface implementation into PricingEngine::Engine, and Sorbet interfaces and function signatures enforce implemented methods and expected return types. Testing separates gem-isolated tests using repository mocks, consumer unit tests for repository behavior, and integration tests confirming the gem works within each consumer. The result, according to the post, is removal of the circular dependency and a typed contract that makes consumers responsible for data access."
---

# Remove Circular Dependencies by Using Dependency Injection and the Repository Pattern in Ruby

[Shopify](https://yomu.fyi/company/shopify) · 2023-10-18 · Mar 19, 2021

**Type:** Tutorial

## Summary

An internal Ruby pricing gem used by Shopify Core and Storefront Renderer needed consumer-owned data while retaining shared pricing knowledge, creating a circular dependency. The proposed design puts calculation logic and shared domain types in a stateless gem, while each consumer implements a repository contract for retrieving and returning the required data. Constructor injection passes a PricingRepositoryInterface implementation into PricingEngine::Engine, and Sorbet interfaces and function signatures enforce implemented methods and expected return types. Testing separates gem-isolated tests using repository mocks, consumer unit tests for repository behavior, and integration tests confirming the gem works within each consumer. The result, according to the post, is removal of the circular dependency and a typed contract that makes consumers responsible for data access.

## Context

The pricing gem contains shared calculation knowledge, while Shopify Core and Storefront Renderer own the required data in different shapes and data sources. Requiring the gem and consumers to communicate directly created a circular dependency, and the gem should not know how consumers query data.

## Approach / What changed

Define repository interfaces and common domain objects in the gem, have each consumer implement the required repository, and pass that implementation into PricingEngine::Engine through constructor injection. Sorbet interfaces and function signatures enforce the contract, while mocks, unit tests, and integration tests cover the gem and its consumers.

## Takeaways

- The repository contract keeps pricing logic in the gem while leaving data storage and access to each consumer.
- Constructor injection supplies PricingEngine::Engine with a PricingRepositoryInterface implementation instead of letting the gem query consumers directly.
- Sorbet interfaces and function signatures require repository methods and expected return types, helping consumers conform to the gem’s contract.

**Tags:** [Architecture](https://yomu.fyi/topic/architecture), [Ruby](https://yomu.fyi/topic/ruby), [Testing](https://yomu.fyi/topic/testing)

- Source: [Shopify](https://shopify.engineering/repository-pattern-ruby)
- Source URL: https://shopify.engineering/repository-pattern-ruby
- Ingested by Yomu: 2026-08-31T01:10:01.295Z

[Read original post](https://shopify.engineering/repository-pattern-ruby)
