Version 4.0

Repositories » Writing Aggregates

Repositories provide a command builder interface which you can use to construct complex commands that can persist nested data, which reflect aggregate structures.

This API is limited to Create commands only. Use changesets if you need more flexibility and control

has_many example

Let's say we have :users with :tasks and we'd like to persist a nested data structure which represents this association.

require 'rom-repository'

rom = ROM.container(:sql, 'sqlite::memory') do |config|
  config.default.create_table(:users) do
    primary_key :id
    column :name, String, null: false
    column :email, String, null: false
  end

  config.default.create_table(:tasks) do
    primary_key :id
    foreign_key :user_id, :users
    column :title, String, null: false
  end

  config.relation(:users) do
    schema(infer: true) do
      associations do
        has_many :tasks
      end
    end
  end

  config.relation(:tasks) do
    schema(infer: true) do
      associations do
        belongs_to :user
      end
    end
  end
end

Once we establish canonical associations for our relations, repositories will know how to prepare commands for persisting nested data.

Let's define a repository which exposes an interface for persisting a new user along with associated tasks:

class UserRepo < ROM::Repository[:users]
  def create_with_tasks(user)
    users.combine(:tasks).command(:create).call(user)
  end
end

user_repo = UserRepo.new(rom)

user_repo.create_with_tasks(
  name: 'Jane',
  email: 'jane@doe.org',
  tasks: [{ title: 'Task 1' }, { title: 'Task 2' }]
)
# => #<ROM::Struct[User] id=1 name="Jane" email="jane@doe.org" tasks=[#<ROM::Struct[Task] id=1 user_id=1 title="Task 1">, #<ROM::Struct[Task] id=2 user_id=1 title="Task 2">]>