Skip to content

Instantly share code, notes, and snippets.

@lasseebert
Last active January 8, 2018 21:52
Show Gist options
  • Save lasseebert/18133670d77d080d7739d428b802752d to your computer and use it in GitHub Desktop.
Save lasseebert/18133670d77d080d7739d428b802752d to your computer and use it in GitHub Desktop.

Isolating code in contexts

Based on my blog post

Agenda

  • What?
  • How?
  • Why?
  • Elixir specific stuff
    • Module naming
    • OTP
  • Show-and-tell example code

What?

It is a pattern much similar to the Facade Pattern.

You probably already use it to some extend.

Diffs from Facade:

  • Only isolates internal code
  • Also isolates on file system level
  • A Context covers a specific logical area of the application. A facade covers a specific technical area.

How?

Pretty simple:

Refactoring existing code:

  • Find a group of files that logically belong together
  • Put them in the same dir and namespace
  • Minimize public API by creating the context module
    • Also minimize public types and structs

From scratch:

  • After a few refactorings, it will become natural to isolate in contexts and benefits will be greater
  • Profit

Why?

Immediate benefits of contexts:

  • Smaller public API. Easier to call the code.
  • Easier to understand a context as a logical chunk of the application.
  • Easier to understand the entire codebase as a collection of contexts

Long term benefits:

  • Explicit dependencies
  • Explicit types and structs
  • Forces developers to think about important stuff
  • Easier to maintain and change the internal code or db structure.
  • Easy to delete the entire context

Visualized:

deps_no_contexts

VS

deps_contexts

Elixir specific stuff

Module names

File structure

+ some_context
|-- some_context.ex
|-- some_thing.ex

Public/main module: MyApp.SomeContext Inner module: MyApp.SomeContext.SomeThing

OTP

Since Elixir 1.5, simple add main module to supervisor:

children = [
  SomeContext,
  SomeOtherContext
]

Supervisor.start_link(children, strategy: :one_for_one)

And then implement a child_spec in the main module:

def child_spec([]) do
  %{
    id: __MODULE__,
    start: {SomeWorker, :start_link, []}
  }
end

Or if starting a supervisor:

def child_spec([]) do
  %{
    id: __MODULE__,
    start: {SomeSupervisor, :start_link, []},
    type: :supervisor
  }
end

Example

Legolas.Geolocation

Further reading:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment