ores.cpp.service.service_impl
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>
{{#has_uuid_include}}
#include <boost/uuid/uuid_io.hpp>
{{/has_uuid_include}}
<<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_);
}
{{#has_as_of_lookup}}
std::vector<domain::{{entity_singular}}>
{{entity_singular}}_service::list_{{entity_plural_short}}_at_timepoint(
const std::string& as_of, const std::string& {{primary_key.column}}) {
BOOST_LOG_SEV(lg(), debug) << "Listing {{entity_plural_words}} at timepoint: " << as_of;
if ({{primary_key.column}}.empty())
return repo_.read_at_timepoint(ctx_, as_of);
return repo_.read_at_timepoint(ctx_, as_of, {{primary_key.column}});
}
{{/has_as_of_lookup}}
{{#foreign_keys}}
{{#list_by}}
std::vector<domain::{{entity_singular}}>
{{entity_singular}}_service::list_{{entity_plural_short}}_by_{{column}}(const std::string& {{column}},
std::uint32_t offset, std::uint32_t limit) {
BOOST_LOG_SEV(lg(), debug) << "Listing {{entity_plural_words}} by {{column}}: " << {{column}};
return repo_.read_latest_by_{{column}}(ctx_, {{column}}, offset, limit);
}
std::uint32_t {{entity_singular}}_service::count_{{entity_plural_short}}_by_{{column}}(const std::string& {{column}}) {
BOOST_LOG_SEV(lg(), debug) << "Getting total {{entity_plural_words}} count by {{column}}: " << {{column}};
return repo_.get_total_{{entity_singular_short}}_count_by_{{column}}(ctx_, {{column}});
}
{{#list_by_uuid}}
std::vector<domain::{{entity_singular}}>
{{entity_singular}}_service::list_{{entity_plural_short}}_by_{{column}}(const boost::uuids::uuid& {{column}},
std::uint32_t offset, std::uint32_t limit) {
BOOST_LOG_SEV(lg(), debug) << "Listing {{entity_plural_words}} by {{column}}: " << {{column}};
return repo_.read_latest_by_{{column}}(ctx_, boost::uuids::to_string({{column}}), offset, limit);
}
std::uint32_t {{entity_singular}}_service::count_{{entity_plural_short}}_by_{{column}}(const boost::uuids::uuid& {{column}}) {
BOOST_LOG_SEV(lg(), debug) << "Getting total {{entity_plural_words}} count by {{column}}: " << {{column}};
return repo_.get_total_{{entity_singular_short}}_count_by_{{column}}(ctx_, boost::uuids::to_string({{column}}));
}
{{/list_by_uuid}}
{{/list_by}}
{{#list_by_as_of}}
std::vector<domain::{{entity_singular}}>
{{entity_singular}}_service::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) {
BOOST_LOG_SEV(lg(), debug) << "Listing {{entity_plural_words}} by {{column}} as of window: " << {{column}};
return repo_.read_by_{{column}}_as_of(ctx_, {{column}}, valid_from_bound, valid_to_bound);
}
{{/list_by_as_of}}
{{/foreign_keys}}
{{#has_audit_columns}}
std::optional<domain::{{entity_singular}}>
{{entity_singular}}_service::get_{{entity_singular_short}}_at_version({{{primary_key.params}}},
std::uint32_t version) {
BOOST_LOG_SEV(lg(), debug) << "Getting {{entity_singular_words}} at version. " << {{{primary_key.log_fields}}}
<< " version: " << version;
return repo_.read_at_version(ctx_, {{{primary_key.args}}}, version);
}
{{/has_audit_columns}}
std::optional<domain::{{entity_singular}}>
{{entity_singular}}_service::{{#service_find_prefix}}find_{{/service_find_prefix}}{{^service_find_prefix}}get_{{/service_find_prefix}}{{entity_singular_short}}({{{primary_key.params}}}) {
BOOST_LOG_SEV(lg(), debug) << "{{#service_find_prefix}}Finding{{/service_find_prefix}}{{^service_find_prefix}}Getting{{/service_find_prefix}} {{entity_singular_words}}. " << {{{primary_key.log_fields}}};
auto results = repo_.read_latest(ctx_, {{{primary_key.args}}});
if (results.empty()) return std::nullopt;
return results.front();
}
{{#service_find_by_uuid}}
std::optional<domain::{{entity_singular}}>
{{entity_singular}}_service::find_{{entity_singular_short}}(const boost::uuids::uuid& {{primary_key.column}}) {
BOOST_LOG_SEV(lg(), debug) << "Finding {{entity_singular_words}}. " << "{{primary_key.column}}: " << {{primary_key.column}};
auto results = repo_.read_latest(ctx_, boost::uuids::to_string({{primary_key.column}}));
if (results.empty()) return std::nullopt;
return results.front();
}
{{/service_find_by_uuid}}
{{#service_find_by_code}}
{{#parent_column}}
std::optional<domain::{{entity_singular}}>
{{entity_singular}}_service::find_{{entity_singular_short}}_by_code(const boost::uuids::uuid& {{parent_column}},
const std::string& {{column}}) {
BOOST_LOG_SEV(lg(), debug) << "Finding {{entity_singular_words}} by {{parent_column}}/{{column}}: "
<< {{parent_column}} << "/" << {{column}};
auto results = repo_.read_latest_by_code(ctx_, boost::uuids::to_string({{parent_column}}), {{column}});
if (results.empty()) return std::nullopt;
return results.front();
}
{{/parent_column}}
{{^parent_column}}
std::optional<domain::{{entity_singular}}>
{{entity_singular}}_service::find_{{entity_singular_short}}_by_code(const std::string& {{column}}) {
BOOST_LOG_SEV(lg(), debug) << "Finding {{entity_singular_words}} by {{column}}: " << {{column}};
auto results = repo_.read_latest_by_code(ctx_, {{column}});
if (results.empty()) return std::nullopt;
return results.front();
}
{{/parent_column}}
{{/service_find_by_code}}
{{#has_batch_read}}
std::vector<domain::{{entity_singular}}>
{{entity_singular}}_service::get_{{entity_plural_short}}({{{primary_key.batch_params}}}) {
return repo_.read_latest(ctx_, {{{primary_key.batch_args}}});
}
{{/has_batch_read}}
void {{entity_singular}}_service::save_{{entity_singular_short}}(const domain::{{entity_singular}}& v) {
{{#primary_key.is_uuid}}
if (v.{{#primary_key.is_identity_group_column}}identity.{{/primary_key.is_identity_group_column}}{{primary_key.column}}.is_nil())
throw std::invalid_argument("{{entity_title}} {{primary_key.column}} cannot be empty.");
{{/primary_key.is_uuid}}
{{^primary_key.is_uuid}}
{{#primary_key.columns}}
if (v.{{#is_identity_group_column}}identity.{{/is_identity_group_column}}{{column}}.empty())
throw std::invalid_argument("{{entity_title}} {{column}} cannot be empty.");
{{/primary_key.columns}}
{{/primary_key.is_uuid}}
BOOST_LOG_SEV(lg(), debug) << "Saving {{entity_singular_words}}. " << {{{primary_key.value_log_fields}}};
auto t = v;
stamp(t, ctx_);
repo_.write(ctx_, t);
BOOST_LOG_SEV(lg(), info) << "Saved {{entity_singular_words}}. " << {{{primary_key.value_log_fields}}};
}
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.is_identity_group_column}}identity.{{/primary_key.is_identity_group_column}}{{primary_key.column}}.is_nil())
throw std::invalid_argument("{{entity_title}} {{primary_key.column}} cannot be empty.");
{{/primary_key.is_uuid}}
{{^primary_key.is_uuid}}
for (const auto& e : {{entity_plural_short}}) {
{{#primary_key.columns}}
if (e.{{#is_identity_group_column}}identity.{{/is_identity_group_column}}{{column}}.empty())
throw std::invalid_argument("{{entity_title}} {{column}} cannot be empty.");
{{/primary_key.columns}}
}
{{/primary_key.is_uuid}}
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}}({{{primary_key.params}}}) {
BOOST_LOG_SEV(lg(), debug) << "Removing {{entity_singular_words}}. " << {{{primary_key.log_fields}}};
repo_.remove(ctx_, {{{primary_key.args}}});
BOOST_LOG_SEV(lg(), info) << "Removed {{entity_singular_words}}. " << {{{primary_key.log_fields}}};
}
{{#service_find_by_uuid}}
void {{entity_singular}}_service::remove_{{entity_singular_short}}(const boost::uuids::uuid& {{primary_key.column}}) {
BOOST_LOG_SEV(lg(), debug) << "Removing {{entity_singular_words}}. " << "{{primary_key.column}}: " << {{primary_key.column}};
repo_.remove(ctx_, boost::uuids::to_string({{primary_key.column}}));
BOOST_LOG_SEV(lg(), info) << "Removed {{entity_singular_words}}. " << "{{primary_key.column}}: " << {{primary_key.column}};
}
{{/service_find_by_uuid}}
void {{entity_singular}}_service::delete_{{entity_plural_short}}({{{primary_key.batch_params}}}) {
repo_.remove(ctx_, {{{primary_key.batch_args}}});
}
std::vector<domain::{{entity_singular}}>
{{entity_singular}}_service::get_{{entity_singular_short}}_history({{{primary_key.params}}}) {
BOOST_LOG_SEV(lg(), debug) << "Getting history for {{entity_singular_words}}. " << {{{primary_key.log_fields}}};
return repo_.read_all(ctx_, {{{primary_key.args}}});
}
{{#service_find_by_uuid}}
std::vector<domain::{{entity_singular}}>
{{entity_singular}}_service::get_{{entity_singular_short}}_history(const boost::uuids::uuid& {{primary_key.column}}) {
BOOST_LOG_SEV(lg(), debug) << "Getting history for {{entity_singular_words}}. " << "{{primary_key.column}}: " << {{primary_key.column}};
return repo_.read_all(ctx_, boost::uuids::to_string({{primary_key.column}}));
}
{{/service_find_by_uuid}}
{{#has_parent_id}}
std::vector<ores::utility::domain::hierarchy_node>
{{entity_singular}}_service::get_hierarchy(const boost::uuids::uuid& root_id, bool from_root) {
BOOST_LOG_SEV(lg(), debug) << "Getting hierarchy for {{entity_singular}} root: " << root_id;
auto rows = repo_.get_hierarchy(ctx_, root_id, from_root);
return ores::utility::domain::build_tree(rows);
}
{{/has_parent_id}}
<<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}});
}
{{#left.list_by}}
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}}
std::uint32_t offset, std::uint32_t limit) {
BOOST_LOG_SEV(lg(), debug) << "Listing {{name_words}} for {{left.column_title_lower}}: " << {{left.column}}
<< " offset: " << offset << " limit: " << limit;
return repo_.read_latest_by_{{left.column_short}}({{left.column}}, offset, limit);
}
std::uint32_t {{name_singular}}_service::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}}
return repo_.get_total_{{name_singular_short}}_count_by_{{left.column_short}}({{left.column}});
}
{{/left.list_by}}
{{#right.list_by}}
std::vector<domain::{{name_singular}}>
{{#right.is_uuid}}
{{name_singular}}_service::list_{{name_short}}_by_{{right.column_short}}(const boost::uuids::uuid& {{right.column}},
{{/right.is_uuid}}
{{^right.is_uuid}}
{{name_singular}}_service::list_{{name_short}}_by_{{right.column_short}}(const std::string& {{right.column}},
{{/right.is_uuid}}
std::uint32_t offset, std::uint32_t limit) {
BOOST_LOG_SEV(lg(), debug) << "Listing {{name_words}} for {{right.column_title_lower}}: " << {{right.column}}
<< " offset: " << offset << " limit: " << limit;
return repo_.read_latest_by_{{right.column_short}}({{right.column}}, offset, limit);
}
std::uint32_t {{name_singular}}_service::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}}
return repo_.get_total_{{name_singular_short}}_count_by_{{right.column_short}}({{right.column}});
}
{{/right.list_by}}
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}}
{{#left.is_date}}
if (!{{name_singular_short}}.{{left.column}}.ok()) {
{{/left.is_date}}
{{^left.is_date}}
if ({{name_singular_short}}.{{left.column}}.empty()) {
{{/left.is_date}}
{{/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}}
{{#right.is_date}}
if (!{{name_singular_short}}.{{right.column}}.ok()) {
{{/right.is_date}}
{{^right.is_date}}
if ({{name_singular_short}}.{{right.column}}.empty()) {
{{/right.is_date}}
{{/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
- Parent facet: ores.cpp.service
- Template variable reference