# Glossary

We are trying to make the purpose and best use of *svcs* as clear as possible, even if you're unfamiliar with all the concepts it's based on.
As such, the glossary got slightly out of hand, but we hope it's to your advantage.

If you want a more narrative introduction into the concepts, we strongly recommend reading the wonderful book [*Architecture Patterns with Python*] (you can read it for free [on the web](https://www.cosmicpython.com/book/preface.html)).

:::{glossary}

Service
    Unfortunately, a "service" is a highly overloaded term in software engineering, but we've resisted to come up with a new term that's even more confusing.

    In our context, it's a *local* object managed by *svcs*.
    This can be anything you'd like to be loosely coupled to your application code.
    For example, a database connection, a web API client, or a cache.

    They're usually also something you need to configure before using and can't just instantiate directly in your business code.

    ::: {important}
    One key aspect of these services is that they provide **behavior**, but have **no state** that is relevant to the business logic.
    They are pure *doers*.
    They query databases, make HTTP requests, store data in caches, or delete files.

    But they don't do anything business-relevant neither with the data they send out, nor with the data they get back.

    In an ideal world, they only pass data hence and forth with the domain model which decides what happens next based on plain objects.
    :::


Resource
    Used interchangeably with {term}`service`, but also a heavily overloaded term.


Service Layer
    The service layer is where your business logic (also known as the *domain model*) meets your {term}`service`s.

    Since services can use other services, it's not a flat layer but more of a tree.
    The entry point is called from your {term}`composition root` (e.g., your web framework's views) and coordinates database transactions, other services and, the domain model.

    If you pass in all the services it needs, it's *{term}`dependency injection`*.
    If you look up the services *within* the service layer, it's *{term}`service location`*.

    An example would be a function for adding users to organizations that takes a database [Unit of Work](https://www.cosmicpython.com/book/chapter_06_uow.html), an email notification queue, an organization ID and a user ID as parameters and queries the domain model if it's OK:

    ```python
    def add_user_to_org(unit_of_work, mail_q, org_id, user_id):
        """
        Service Layer entry point.
        """
        with unit_of_work:
            org = unit_of_work.organizations.get(org_id)
            user = unit_of_work.users.get(user_id)

            domain_model.check_if_can_add_user_to_org(org, user)

            unit_of_work.orgs.add_user(org_id, user_id)
            mail_q.send_welcome_mail(user.email, user.name, org.name)

            unit_of_work.commit()
    ```

    In this case, the `unit_of_work` and `mail_q` parameters are services that are used by the service `add_user_to_org()` and are passed – or: *injected* – by the {term}`composition root`.

    The business rules are enforced by `domain_model.check_if_can_add_user_to_org()` which is a pure function working on plain domain objects and doesn't use any services.

    ---

    As you can see, ideally you call service layer functions from your views and pass them all the services it needs to do their job.
    That keeps the service layer clean from any framework-specific code and makes it easy to test.
    You could also call that function from a CLI entry point, a work queue, or a test.

    Admittedly, in simple cases it's possible that the whole logic is in the service layer and you don't need a separate domain model.
    Don't let that discourage you.

    ::: {seealso}
    The fourth chapter of the wonderful [*Architecture Patterns with Python*] book called [*Flask API and Service Layer*](https://www.cosmicpython.com/book/chapter_04_service_layer.html) (you can read it for free on the web).
    :::


Service Location
    See {term}`Service Locator`.

Service Locator
    The [architectural pattern](https://en.wikipedia.org/wiki/Architectural_pattern) implemented by *svcs*.

    **Like** {term}`dependency injection`, it's a way to achieve loose coupling between your business code and the services it depends on by having a central registry of factories for those services.

    **Unlike** dependency injection, it's *imperative* (and not declarative), as you ask for services explicitly at runtime instead of having them injected into your business code based on function parameter types, or similar.

    That usually requires less opaque magic since nothing meddles with your function/method definitions.

    The active acquisition of services by calling `get()` when you *know* for sure you're going to need it avoids the conundrum of either having to pass a factory (e.g., a connection pool – which also puts the onus of cleanup on you) or eagerly creating services that you never use:

    % skip: next

    ```python
    def view(request):
        if request.form.valid():
            # Form is valid; only NOW get a DB connection
            # and pass it into your service layer.
            return handle_form_data(
                request.services.get(Database),
                form.data,
            )

        raise InvalidFormError()
    ```

    ::: {important}
    If you use *svcs* like in the example above, you're actually doing {term}`dependency injection` – and that's a Good Thing™.

    Obtaining the database using `request.services.get()` *is* service location, but passing it into your {term}`service layer` without using it yourself makes the view a {term}`composition root` and `handle_form_data()` the entry point into your service layer.

    We strongly recommend using *svcs* like this.
    :::

    Classic service location in the service layer would look something like this:

    ```python
    from flask import request

    def view():
        if request.form.valid():
            return handle_form_data(form.data)

    def handle_form_data(data):
        svcs.flask.get(Database).do_database_stuff(data)
    ```

    We do not recommend using *svcs* like this because it's harder to test and reason about.

    ::: {seealso}
    <https://en.wikipedia.org/wiki/Service_locator_pattern>.
    :::


Dependency Injection
    Dependency Injection means that the {term}`service layer` is called with all services it needs to do its job.
    It is a fundamental technique to achieve loose coupling between your business code and the services it depends on – and to achieve {term}`Inversion of Control`.

    Often when talking about dependency injection, people think of *dependency injection frameworks* that use decorators or other magic to inject services into their code.
    But *dependency injection* just means that services are passed from the outside, with no control over how that happens (hence {term}`Inversion of Control`).

    Our preferred way to use *svcs* is to use it as a {term}`service locator`, but *only* in your {term}`composition root` to acquire necessary services.
    Which then injects said services into your {term}`service layer`:

    ```python
    def view(request):
        """
        View and composition root.
        """
        return do_something(svcs_from(request).get(Database))

    def do_something(db):
        """
        Service layer.
        """
        db.do_database_stuff()
    ```

    In this case `view` uses *svcs* to *locate* a `Database` and then *injects* it into `do_something()`.
    `do_something()` doesn't know where `db` is coming from and how it was created.
    It only knows it needs to do database stuff.

    ::: {seealso}
    - <https://en.wikipedia.org/wiki/Dependency_injection>
    - [*incant*](https://github.com/Tinche/incant), a lovely package that implements dependency injection in Python.
    :::


Composition Root
    The place that acquires all the services your application needs and calls into the {term}`service layer`.

    Common types are web framework views, CLI command entry points, or test fixtures.

    We recommend using *svcs* in your composition roots to get all the services you need and then pass them into your {term}`service layer`.


IoC
    See {term}`Inversion of Control`.

Inversion of Control
    Inversion of Control is too complicated and essential to explain in a few sentences.
    So if you feel like you haven't fully grokked it, we *strongly* recommend consulting the links at the end of this entry.

    It's about accepting that separating a big ball of mud into small modules is still a lot of dirt, just spread out – which is worse.
    Instead, it's necessary to break the coupling between classes and modules – and achieve loose coupling.

    The most widespread method to achieve that is {term}`Dependency Injection`.

    ::: {seealso}

    - [*What is Inversion of Control and Why Does it Matter?*](https://seddonym.me/2019/04/15/inversion-of-control/)

    - [*Three Techniques for Inverting Control, in Python*](https://seddonym.me/2019/08/03/ioc-techniques/)

    - [*Hoist Your I/O*](https://www.youtube.com/watch?v=PBQN62oUnN8) – a 2015 talk by Brandon Rhodes.

    :::


Dependency Inversion Principle
    Sometimes confused with {term}`Dependency Injection` due to the similarity of "Injection" and "Inversion", but only tangentially related.
    It's the D in [SOLID](https://en.wikipedia.org/wiki/SOLID) and also known as "*program against interfaces, not implementations*".

    The goal is to achieve loose coupling by making code depend on interfaces instead of concrete implementations.

    Since Python is dynamic and you're free to ignore type hints in your tests, the usefulness isn't quite as high as in more static languages.
    Nobody will stop you from passing a Mock where a Django `QuerySet` is expected.

    ::: {seealso}
    - {doc}`typing-caveats` regarding abstract classes and *svcs*.

    - <https://en.wikipedia.org/wiki/Dependency_inversion_principle>

    - The third chapter of the wonderful [*Architecture Patterns with Python*] book called [*On Coupling and Abstractions*](https://www.cosmicpython.com/book/chapter_03_abstractions.html) (you can read it for free on the web).
    :::


Hexagonal Architecture
    Also known as "*ports and adapters*", "*onion architecture*", or "*clean architecture*".
    They all have slightly different meaning, but the core idea is the same.

    It divides a system into several loosely-coupled interchangeable components, such as the database, the user interface, test scripts, and interfaces with other systems (also known as services) with the application core (also known as business code) in the middle.

    The business code doesn't use those services directly, but only through interfaces that are called *ports* here.

    Now a service locator like *svcs* can be used to register factories for those interfaces such that the business code can use the services as *adapters* to the real world (e.g., query a database) without knowing what they are and where they come from.

    It is therefore a special case of the {term}`Dependency Inversion Principle`.

    The *svcs* logo is a hexagon to remind you of this architecture.

    ::: {seealso}

    - <https://en.wikipedia.org/wiki/Hexagonal_architecture_(software)>

    - <https://alistair.cockburn.us/hexagonal-architecture/>

    - [*Functional Core, Imperative Shell*](https://www.destroyallsoftware.com/screencasts/catalog/functional-core-imperative-shell) – a screencast by Gary Bernhardt.

    - [*The Clean Architecture in Python*](https://www.youtube.com/watch?v=DJtef410XaM) – a 2014 talk by Brandon Rhodes.

    :::


Service Discovery
    An completely unrelated concept for finding *remote* services (e.g., web services, database servers, ...).

    Another sad consequence of the overloaded term {term}`service`.

    Common examples of implementations are [Consul](https://www.consul.io), [*etcd*](https://etcd.io), or [Apache ZooKeeper](https://zookeeper.apache.org).
:::


[*Architecture Patterns with Python*]: https://www.oreilly.com/library/view/architecture-patterns-with/9781492052197/
