Archetype: cpp_service.cpp.mustache

Table of Contents

Repository-backed operations exposed to messaging. 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.cpp.mustache.

{{! GENERATED FILE — tangled from projects/ores.codegen/library/templates/cpp_messaging.org. Edit the org source. }}
{{{cpp_license}}}
{{#domain_entity}}
#include "ores.{{component_core}}/service/{{entity_singular}}_service.hpp"

#include <cstdint>
#include <stdexcept>
<<paste:A7D3F1E2-8C5B-4A2F-B9E0-3D6C1A8F4B2E>>
#include "ores.service/messaging/handler_helpers.hpp"

using ores::service::messaging::stamp;

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

using namespace ores::logging;

{{entity_singular}}_service::{{entity_singular}}_service(context ctx)
    : ctx_(std::move(ctx))
<<paste:4F8A2B1E-7C3D-4E9F-B6A0-1D5C8F2E7A4B>>{}

std::vector<domain::{{entity_singular}}> {{entity_singular}}_service::list_{{entity_plural_short}}(std::uint32_t offset, std::uint32_t limit) {
    BOOST_LOG_SEV(lg(), debug) << "Listing all {{entity_plural_words}}";
    return repo_.read_latest(ctx_, offset, limit);
}

std::uint32_t {{entity_singular}}_service::count_{{entity_plural_short}}() {
    BOOST_LOG_SEV(lg(), debug) << "Getting total {{entity_plural_words}} count";
    return repo_.get_total_{{entity_singular_short}}_count(ctx_);
}

std::optional<domain::{{entity_singular}}>
{{entity_singular}}_service::get_{{entity_singular_short}}(const std::string& {{primary_key.column}}) {
    BOOST_LOG_SEV(lg(), debug) << "Getting {{entity_singular_words}}: " << {{primary_key.column}};
    auto results = repo_.read_latest(ctx_, {{primary_key.column}});
    if (results.empty()) return std::nullopt;
    return results.front();
}

void {{entity_singular}}_service::save_{{entity_singular_short}}(const domain::{{entity_singular}}& v) {
{{#primary_key.is_uuid}}
    if (v.{{primary_key.column}}.is_nil())
{{/primary_key.is_uuid}}
{{^primary_key.is_uuid}}
    if (v.{{primary_key.column}}.empty())
{{/primary_key.is_uuid}}
        throw std::invalid_argument("{{entity_title}} {{primary_key.column}} cannot be empty.");
    BOOST_LOG_SEV(lg(), debug) << "Saving {{entity_singular_words}}: " << v.{{primary_key.column}};
    auto t = v;
    stamp(t, ctx_);
    repo_.write(ctx_, t);
    BOOST_LOG_SEV(lg(), info) << "Saved {{entity_singular_words}}: " << v.{{primary_key.column}};
}

void {{entity_singular}}_service::save_{{entity_plural_short}}(
    const std::vector<domain::{{entity_singular}}>& {{entity_plural_short}}) {
{{#primary_key.is_uuid}}
    for (const auto& e : {{entity_plural_short}})
        if (e.{{primary_key.column}}.is_nil())
{{/primary_key.is_uuid}}
{{^primary_key.is_uuid}}
    for (const auto& e : {{entity_plural_short}})
        if (e.{{primary_key.column}}.empty())
{{/primary_key.is_uuid}}
            throw std::invalid_argument("{{entity_title}} {{primary_key.column}} cannot be empty.");
    BOOST_LOG_SEV(lg(), debug) << "Saving " << {{entity_plural_short}}.size()
        << " {{entity_plural_words}}";
    auto ts = {{entity_plural_short}};
    for (auto& e : ts)
        stamp(e, ctx_);
    repo_.write(ctx_, ts);
}

void {{entity_singular}}_service::delete_{{entity_singular_short}}(const std::string& {{primary_key.column}}) {
    BOOST_LOG_SEV(lg(), debug) << "Removing {{entity_singular_words}}: " << {{primary_key.column}};
    repo_.remove(ctx_, {{primary_key.column}});
    BOOST_LOG_SEV(lg(), info) << "Removed {{entity_singular_words}}: " << {{primary_key.column}};
}

void {{entity_singular}}_service::delete_{{entity_plural_short}}(
    const std::vector<std::string>& {{primary_key.column}}s) {
    repo_.remove(ctx_, {{primary_key.column}}s);
}

std::vector<domain::{{entity_singular}}>
{{entity_singular}}_service::get_{{entity_singular_short}}_history(const std::string& {{primary_key.column}}) {
    BOOST_LOG_SEV(lg(), debug) << "Getting history for {{entity_singular_words}}: " << {{primary_key.column}};
    return repo_.read_all(ctx_, {{primary_key.column}});
}

<<paste:D3E7A5F2-8B4C-4D1E-9A6F-2E1C7B4D8A3F>>
}
{{/domain_entity}}
{{#junction}}
#include "ores.{{component_core}}/service/{{name_singular}}_service.hpp"

#include <stdexcept>
{{#has_uuid_left_or_right}}
#include <boost/uuid/uuid_io.hpp>
{{/has_uuid_left_or_right}}

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

using namespace ores::logging;

{{name_singular}}_service::{{name_singular}}_service(context ctx)
    : repo_(ctx) {}

std::vector<domain::{{name_singular}}> {{name_singular}}_service::list_{{name_short}}() {
    BOOST_LOG_SEV(lg(), debug) << "Listing all {{name_words}}";
    return repo_.read_latest();
}

std::vector<domain::{{name_singular}}>
{{#left.is_uuid}}
{{name_singular}}_service::list_{{name_short}}_by_{{left.column_short}}(const boost::uuids::uuid& {{left.column}}) {
{{/left.is_uuid}}
{{^left.is_uuid}}
{{name_singular}}_service::list_{{name_short}}_by_{{left.column_short}}(const std::string& {{left.column}}) {
{{/left.is_uuid}}
    BOOST_LOG_SEV(lg(), debug) << "Listing {{name_words}} for {{left.column_title_lower}}: " << {{left.column}};
    return repo_.read_latest_by_{{left.column_short}}({{left.column}});
}

void {{name_singular}}_service::save_{{name_singular_short}}(const domain::{{name_singular}}& {{name_singular_short}}) {
{{#left.is_uuid}}
    if ({{name_singular_short}}.{{left.column}}.is_nil()) {
{{/left.is_uuid}}
{{^left.is_uuid}}
    if ({{name_singular_short}}.{{left.column}}.empty()) {
{{/left.is_uuid}}
        throw std::invalid_argument("{{left.column_title}} cannot be empty.");
    }
{{#right.is_uuid}}
    if ({{name_singular_short}}.{{right.column}}.is_nil()) {
{{/right.is_uuid}}
{{^right.is_uuid}}
    if ({{name_singular_short}}.{{right.column}}.empty()) {
{{/right.is_uuid}}
        throw std::invalid_argument("{{right.column_title}} cannot be empty.");
    }
    BOOST_LOG_SEV(lg(), debug) << "Saving {{name_singular_words}}: " << {{name_singular_short}}.{{left.column}}
                               << "/" << {{name_singular_short}}.{{right.column}};
    repo_.write({{name_singular_short}});
    BOOST_LOG_SEV(lg(), info) << "Saved {{name_singular_words}}: " << {{name_singular_short}}.{{left.column}}
                              << "/" << {{name_singular_short}}.{{right.column}};
}

{{#left.is_uuid}}
void {{name_singular}}_service::remove_{{name_singular_short}}(const boost::uuids::uuid& {{left.column}},
{{/left.is_uuid}}
{{^left.is_uuid}}
void {{name_singular}}_service::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}}
    BOOST_LOG_SEV(lg(), debug) << "Removing {{name_singular_words}}: " << {{left.column}}
                               << "/" << {{right.column}};
    repo_.remove({{left.column}}, {{right.column}});
    BOOST_LOG_SEV(lg(), info) << "Removed {{name_singular_words}}: " << {{left.column}}
                              << "/" << {{right.column}};
}

}
{{/junction}}

See also

Emacs 29.1 (Org mode 9.6.6)