ores.cpp.service.service_header

Table of Contents

Entity service declarations. service profile (per entity). Application-layer service wrapping repository calls with business-rule validation, NATS event emission, and structured error handling.

See the Template variable reference for the complete list of available variables and their semantics.

Template

The full template source. Edit here and re-tangle with compass build --direct tangle_codegen_templates to regenerate library/templates/cpp_service.hpp.mustache.

{{! GENERATED FILE — tangled from projects/ores.codegen/library/templates/ores.cpp.service.service_header.org. Edit the org source. }}
{{{cpp_license}}}
{{#domain_entity}}
#ifndef ORES_{{component_core_upper}}_SERVICE_{{entity_singular_upper}}_SERVICE_HPP
#define ORES_{{component_core_upper}}_SERVICE_{{entity_singular_upper}}_SERVICE_HPP

#include <chrono>
#include <cstdint>
#include <optional>
#include <string>
#include <vector>
#include "ores.logging/make_logger.hpp"
#include "ores.database/domain/context.hpp"
#include "ores.{{component_include}}/domain/{{entity_singular}}.hpp"
#include "ores.{{component_core}}/repository/{{entity_singular}}_repository.hpp"
#include "ores.{{component_core}}/export.hpp"
{{#has_parent_id}}
#include "ores.utility/domain/hierarchy.hpp"
{{/has_parent_id}}
{{#has_uuid_include}}
#include <boost/uuid/uuid.hpp>
{{/has_uuid_include}}
<<paste:C5F35C7E-6EED-48D8-99F8-793145586882>>

namespace ores::{{component}}::service {

/**
 * @brief Service for managing {{entity_plural_words}}.
 *
 * Provides a higher-level interface for {{entity_singular_words}} operations,
 * wrapping the underlying repository.
 */
class ORES_{{component_core_upper}}_EXPORT {{entity_singular}}_service {
private:
    inline static std::string_view logger_name =
        "ores.{{component}}.service.{{entity_singular}}_service";

    [[nodiscard]] static auto& lg() {
        using namespace ores::logging;
        static auto instance = make_logger(logger_name);
        return instance;
    }

public:
    using context = ores::database::context;

    /**
     * @brief Constructs a {{entity_singular}}_service with a database context.
     *
     * @param ctx The database context for operations.
     */
    explicit {{entity_singular}}_service(context ctx);

    /**
     * @brief Lists {{entity_plural_words}} with pagination support.
     *
     * @param offset Number of records to skip.
     * @param limit Maximum number of records to return.
     * @return Vector of {{entity_plural_words}} for the requested page.
     */
    std::vector<domain::{{entity_singular}}>
    list_{{entity_plural_short}}(std::uint32_t offset, std::uint32_t limit);

    /**
     * @brief Gets the total count of active {{entity_plural_words}}.
     *
     * @return Total number of active {{entity_plural_words}}.
     */
    std::uint32_t count_{{entity_plural_short}}();

{{#has_as_of_lookup}}
    /**
     * @brief Lists {{entity_plural_words}} as they stood at a specific
     * timepoint (valid_from <= as_of < valid_to), possibly filtered by
     * {{primary_key.column}}.
     *
     * @param as_of The timepoint to resolve against.
     * @param {{primary_key.column}} Optional {{primary_key.column}} filter; empty for all.
     * @return Vector of matching {{entity_plural_words}} as of that timepoint.
     */
    std::vector<domain::{{entity_singular}}>
    list_{{entity_plural_short}}_at_timepoint(const std::string& as_of,
                                             const std::string& {{primary_key.column}} = "");
{{/has_as_of_lookup}}

{{#foreign_keys}}
{{#list_by}}
    /**
     * @brief Lists {{entity_plural_words}} filtered by {{column}}, with pagination.
     *
     * @param {{column}} The {{column}} to filter by.
     * @param offset Number of records to skip.
     * @param limit Maximum number of records to return.
     * @return Vector of matching {{entity_plural_words}} for the requested page.
     */
    std::vector<domain::{{entity_singular}}>
    list_{{entity_plural_short}}_by_{{column}}(const std::string& {{column}},
                                              std::uint32_t offset, std::uint32_t limit);

    /**
     * @brief Gets the total count of active {{entity_plural_words}} filtered by {{column}}.
     *
     * @param {{column}} The {{column}} to filter by.
     * @return Total number of matching {{entity_plural_words}}.
     */
    std::uint32_t count_{{entity_plural_short}}_by_{{column}}(const std::string& {{column}});
{{#list_by_uuid}}

    /**
     * @brief Lists {{entity_plural_words}} filtered by {{column}}, with pagination.
     *
     * @param {{column}} The {{column}} to filter by.
     * @param offset Number of records to skip.
     * @param limit Maximum number of records to return.
     * @return Vector of matching {{entity_plural_words}} for the requested page.
     */
    std::vector<domain::{{entity_singular}}>
    list_{{entity_plural_short}}_by_{{column}}(const boost::uuids::uuid& {{column}},
                                              std::uint32_t offset, std::uint32_t limit);

    /**
     * @brief Gets the total count of active {{entity_plural_words}} filtered by {{column}}.
     *
     * @param {{column}} The {{column}} to filter by.
     * @return Total number of matching {{entity_plural_words}}.
     */
    std::uint32_t count_{{entity_plural_short}}_by_{{column}}(const boost::uuids::uuid& {{column}});
{{/list_by_uuid}}
{{/list_by}}

{{#list_by_as_of}}
    /**
     * @brief Lists {{entity_plural_words}} filtered by {{column}} that were live at
     * any point during a parent version's own [valid_from, valid_to) window.
     * See the "Temporal composite entity versioning" architecture doc.
     *
     * @param {{column}} The {{column}} to filter by.
     * @param valid_from_bound The parent version's own valid_from.
     * @param valid_to_bound The parent version's own valid_to.
     * @return Vector of matching {{entity_plural_words}}.
     */
    std::vector<domain::{{entity_singular}}>
    list_{{entity_plural_short}}_by_{{column}}_as_of(const std::string& {{column}},
                                                     std::chrono::system_clock::time_point valid_from_bound,
                                                     std::chrono::system_clock::time_point valid_to_bound);
{{/list_by_as_of}}
{{/foreign_keys}}
{{#has_audit_columns}}
    /**
     * @brief Retrieves a single {{entity_singular_words}} as it stood at a specific
     * version. See the "Temporal composite entity versioning" architecture doc.
     *
     * @param version The version to fetch.
     * @return The {{entity_singular_words}} at that version if found, std::nullopt otherwise.
     */
    std::optional<domain::{{entity_singular}}>
    get_{{entity_singular_short}}_at_version({{{primary_key.params}}}, std::uint32_t version);
{{/has_audit_columns}}

    /**
     * @brief Retrieves a single {{entity_singular_words}} by its primary key.
     *
     * @return The {{entity_singular_words}} if found, std::nullopt otherwise.
     */
    std::optional<domain::{{entity_singular}}>
    {{#service_find_prefix}}find_{{/service_find_prefix}}{{^service_find_prefix}}get_{{/service_find_prefix}}{{entity_singular_short}}({{{primary_key.params}}});
{{#service_find_by_uuid}}

    /**
     * @brief Retrieves a single {{entity_singular_words}} by its uuid primary key.
     *
     * @return The {{entity_singular_words}} if found, std::nullopt otherwise.
     */
    std::optional<domain::{{entity_singular}}>
    find_{{entity_singular_short}}(const boost::uuids::uuid& {{primary_key.column}});
{{/service_find_by_uuid}}
{{#service_find_by_code}}
{{#parent_column}}

    /**
     * @brief Retrieves a single {{entity_singular_words}} by its
     * {{parent_column}} and {{column}} (this entity's natural key is the
     * pair, not {{column}} alone).
     *
     * @return The {{entity_singular_words}} if found, std::nullopt otherwise.
     */
    std::optional<domain::{{entity_singular}}>
    find_{{entity_singular_short}}_by_code(const boost::uuids::uuid& {{parent_column}},
                                             const std::string& {{column}});
{{/parent_column}}
{{^parent_column}}

    /**
     * @brief Retrieves a single {{entity_singular_words}} by its {{column}}.
     *
     * @return The {{entity_singular_words}} if found, std::nullopt otherwise.
     */
    std::optional<domain::{{entity_singular}}>
    find_{{entity_singular_short}}_by_code(const std::string& {{column}});
{{/parent_column}}
{{/service_find_by_code}}
{{#has_batch_read}}

    /**
     * @brief Retrieves a batch of {{entity_plural_words}} by primary key.
     */
    std::vector<domain::{{entity_singular}}>
    get_{{entity_plural_short}}({{{primary_key.batch_params}}});
{{/has_batch_read}}

    /**
     * @brief Saves a {{entity_singular_words}} (creates or updates).
     *
     * @param {{entity_singular_short}} The {{entity_singular_words}} to save.
     * @throws std::exception on failure.
     */
    void save_{{entity_singular_short}}(const domain::{{entity_singular}}& {{entity_singular_short}});

    /**
     * @brief Saves a batch of {{entity_plural_words}}.
     *
     * @param {{entity_plural_short}} The {{entity_plural_words}} to save.
     * @throws std::exception on failure.
     */
    void save_{{entity_plural_short}}(
        const std::vector<domain::{{entity_singular}}>& {{entity_plural_short}});

    /**
     * @brief Deletes a {{entity_singular_words}} by its primary key.
     *
     * @throws std::exception on failure.
     */
    void delete_{{entity_singular_short}}({{{primary_key.params}}});
{{#service_find_by_uuid}}

    /**
     * @brief Removes a {{entity_singular_words}} by its uuid primary key.
     *
     * @throws std::exception on failure.
     */
    void remove_{{entity_singular_short}}(const boost::uuids::uuid& {{primary_key.column}});
{{/service_find_by_uuid}}

    /**
     * @brief Deletes {{entity_plural_words}} by their primary keys.
     */
    void delete_{{entity_plural_short}}({{{primary_key.batch_params}}});

    /**
     * @brief Retrieves all historical versions of a {{entity_singular_words}}.
     */
    std::vector<domain::{{entity_singular}}>
    get_{{entity_singular_short}}_history({{{primary_key.params}}});
{{#service_find_by_uuid}}

    /**
     * @brief Retrieves all historical versions of a {{entity_singular_words}}
     * by its uuid primary key.
     */
    std::vector<domain::{{entity_singular}}>
    get_{{entity_singular_short}}_history(const boost::uuids::uuid& {{primary_key.column}});
{{/service_find_by_uuid}}
{{#has_parent_id}}

    /**
     * @brief Gets the {{entity_singular}} hierarchy (as a forest of trees) rooted
     * at, or containing, the given {{entity_singular}}.
     *
     * @param root_id The {{entity_singular}} to start from.
     * @param from_root If true, returns the whole tree the given node
     * belongs to instead of just its subtree.
     * @return A forest of hierarchy_node trees (normally a single root).
     */
    std::vector<ores::utility::domain::hierarchy_node>
    get_hierarchy(const boost::uuids::uuid& root_id, bool from_root);
{{/has_parent_id}}

<<paste:2D4EA0F9-6AF6-45A5-A959-F672BF866C6A>>
private:
    context ctx_;
    repository::{{entity_singular}}_repository repo_;
<<paste:047A96EF-1CC4-4661-B80B-C6C4532076AB>>
};

}

#endif
{{/domain_entity}}
{{#junction}}
#ifndef ORES_{{component_upper}}_SERVICE_{{name_singular_upper}}_SERVICE_HPP
#define ORES_{{component_upper}}_SERVICE_{{name_singular_upper}}_SERVICE_HPP

#include <string>
#include <vector>
{{#has_uuid_left_or_right}}
#include <boost/uuid/uuid.hpp>
{{/has_uuid_left_or_right}}
#include "ores.logging/make_logger.hpp"
#include "ores.database/domain/context.hpp"
#include "ores.{{component_include}}/domain/{{name_singular}}.hpp"
#include "ores.{{component_core}}/repository/{{name_singular}}_repository.hpp"

namespace ores::{{component}}::service {

/**
 * @brief Service for managing {{name_words}}.
 *
 * This service provides functionality for:
 * - Managing {{name_words}} (CRUD operations)
 */
class {{name_singular}}_service {
private:
    inline static std::string_view logger_name =
        "ores.{{component}}.service.{{name_singular}}_service";

    [[nodiscard]] static auto& lg() {
        using namespace ores::logging;
        static auto instance = make_logger(logger_name);
        return instance;
    }

public:
    using context = ores::database::context;

    /**
     * @brief Constructs a {{name_singular}}_service with required repositories.
     *
     * @param ctx The database context.
     */
    explicit {{name_singular}}_service(context ctx);

    /**
     * @brief Lists all {{name_words}}.
     */
    std::vector<domain::{{name_singular}}> list_{{name_short}}();

    /**
     * @brief Lists {{name_words}} for a specific {{left.column_title_lower}}.
     *
     * @param {{left.column}} The {{left.column_title_lower}} to filter by
     */
    std::vector<domain::{{name_singular}}>
{{#left.is_uuid}}
    list_{{name_short}}_by_{{left.column_short}}(const boost::uuids::uuid& {{left.column}});
{{/left.is_uuid}}
{{^left.is_uuid}}
    list_{{name_short}}_by_{{left.column_short}}(const std::string& {{left.column}});
{{/left.is_uuid}}

{{#left.list_by}}
    /**
     * @brief Lists {{name_words}} for a specific {{left.column_title_lower}}, with pagination.
     */
    std::vector<domain::{{name_singular}}>
{{#left.is_uuid}}
    list_{{name_short}}_by_{{left.column_short}}(const boost::uuids::uuid& {{left.column}},
                                                std::uint32_t offset, std::uint32_t limit);
{{/left.is_uuid}}
{{^left.is_uuid}}
    list_{{name_short}}_by_{{left.column_short}}(const std::string& {{left.column}},
                                                std::uint32_t offset, std::uint32_t limit);
{{/left.is_uuid}}

    /**
     * @brief Gets the total count of active {{name_words}} filtered by {{left.column}}.
     */
    std::uint32_t get_total_{{name_singular_short}}_count_by_{{left.column_short}}(
{{#left.is_uuid}}
        const boost::uuids::uuid& {{left.column}});
{{/left.is_uuid}}
{{^left.is_uuid}}
        const std::string& {{left.column}});
{{/left.is_uuid}}

{{/left.list_by}}
{{#right.list_by}}
    /**
     * @brief Lists {{name_words}} for a specific {{right.column_title_lower}}, with pagination.
     */
    std::vector<domain::{{name_singular}}>
{{#right.is_uuid}}
    list_{{name_short}}_by_{{right.column_short}}(const boost::uuids::uuid& {{right.column}},
                                                 std::uint32_t offset, std::uint32_t limit);
{{/right.is_uuid}}
{{^right.is_uuid}}
    list_{{name_short}}_by_{{right.column_short}}(const std::string& {{right.column}},
                                                 std::uint32_t offset, std::uint32_t limit);
{{/right.is_uuid}}

    /**
     * @brief Gets the total count of active {{name_words}} filtered by {{right.column}}.
     */
    std::uint32_t get_total_{{name_singular_short}}_count_by_{{right.column_short}}(
{{#right.is_uuid}}
        const boost::uuids::uuid& {{right.column}});
{{/right.is_uuid}}
{{^right.is_uuid}}
        const std::string& {{right.column}});
{{/right.is_uuid}}

{{/right.list_by}}
    /**
     * @brief Saves a {{name_singular_words}} (creates or updates).
     *
     * @param {{name_singular_short}} The {{name_singular_words}} to save
     */
    void save_{{name_singular_short}}(const domain::{{name_singular}}& {{name_singular_short}});

    /**
     * @brief Removes a {{name_singular_words}}.
     *
     * @param {{left.column}} The {{left.column_title_lower}}
     * @param {{right.column}} The {{right.column_title_lower}}
     */
{{#left.is_uuid}}
    void remove_{{name_singular_short}}(const boost::uuids::uuid& {{left.column}},
{{/left.is_uuid}}
{{^left.is_uuid}}
    void remove_{{name_singular_short}}(const std::string& {{left.column}},
{{/left.is_uuid}}
{{#right.is_uuid}}
        const boost::uuids::uuid& {{right.column}});
{{/right.is_uuid}}
{{^right.is_uuid}}
        const std::string& {{right.column}});
{{/right.is_uuid}}

private:
    repository::{{name_singular}}_repository repo_;
};

}

#endif
{{/junction}}

See also

Emacs 29.3 (Org mode 9.6.15)