<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Capoerus</title>
    <description>The latest articles on DEV Community by Capoerus (@capoerus).</description>
    <link>https://dev.to/capoerus</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4177395%2F3610552b-5926-4952-8a4c-9b8ed82e485c.png</url>
      <title>DEV Community: Capoerus</title>
      <link>https://dev.to/capoerus</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/capoerus"/>
    <language>en</language>
    <item>
      <title>Beyond `json.encode()`: Building a Type-Aware JSON Conversion Layer in Dart</title>
      <dc:creator>Capoerus</dc:creator>
      <pubDate>Mon, 12 Oct 2026 15:19:48 +0000</pubDate>
      <link>https://dev.to/capoerus/beyond-jsonencode-building-a-type-aware-json-conversion-layer-in-dart-6dp</link>
      <guid>https://dev.to/capoerus/beyond-jsonencode-building-a-type-aware-json-conversion-layer-in-dart-6dp</guid>
      <description>&lt;p&gt;&lt;a href="https://capoerus.com/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fer8jzdohdbp88w20qz7a.png" alt="Logo" width="800" height="885"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Reliable data serialization is one of those architectural concerns that often looks simple until an application grows.&lt;/p&gt;

&lt;p&gt;At first, converting a Dart object into JSON and decoding it back seems straightforward. Dart already provides a native JSON implementation, and for many applications, it is more than enough.&lt;/p&gt;

&lt;p&gt;The challenge begins when an application needs consistent type handling, custom object transformations, database-specific data formats, and predictable behavior across multiple platforms without introducing additional dependencies.&lt;/p&gt;

&lt;p&gt;This is one of the problems I addressed while developing &lt;strong&gt;&lt;code&gt;convert_json&lt;/code&gt;&lt;/strong&gt;, an internal Dart package built for the &lt;a href="https://capoerus.com/" rel="noopener noreferrer"&gt;Capoerus&lt;/a&gt; ecosystem.&lt;/p&gt;

&lt;p&gt;Repository: &lt;a href="https://github.com/Suebersson/convert_json" rel="noopener noreferrer"&gt;https://github.com/Suebersson/convert_json&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The architectural problem: JSON is not the same as a Dart object
&lt;/h2&gt;

&lt;p&gt;JSON supports a limited set of data types: strings, numbers, booleans, null values, arrays, and objects.&lt;/p&gt;

&lt;p&gt;Dart applications work with a much broader set of types, including &lt;code&gt;DateTime&lt;/code&gt;, &lt;code&gt;RegExp&lt;/code&gt;, enums, and custom classes.&lt;/p&gt;

&lt;p&gt;The distinction becomes important when data crosses architectural boundaries.&lt;/p&gt;

&lt;p&gt;Consider this JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"true"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"quantity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"100"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"19.95"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"createdAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-12T15:30:00.000Z"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A standard JSON decoder preserves the quoted values as strings. It cannot automatically know that &lt;code&gt;"true"&lt;/code&gt; represents a boolean, &lt;code&gt;"100"&lt;/code&gt; represents an integer, or the date string should become a &lt;code&gt;DateTime&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is not necessarily a problem with Dart's JSON implementation. It is a consequence of JSON not preserving the original application's type information.&lt;/p&gt;

&lt;p&gt;However, when different parts of an ecosystem repeatedly implement their own conversion rules, maintaining consistent behavior becomes more difficult.&lt;/p&gt;

&lt;p&gt;I wanted a small, dependency-free conversion layer that could centralize these decisions while remaining flexible enough to accommodate application-specific requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I built &lt;code&gt;convert_json&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The package has one responsibility: &lt;strong&gt;encode Dart data into JSON and decode JSON into Dart data, with configurable object transformations.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It is written entirely in Dart and does not depend on third-party packages.&lt;/p&gt;

&lt;p&gt;The package is maintained in a private-by-design development workflow, with its source available in my GitHub repository. I do not intend to publish it on pub.dev.&lt;/p&gt;

&lt;p&gt;This is a deliberate architectural decision. I want the implementation to remain directly available to me and to Capoerus, allowing rapid changes, adjustments, and updates as the ecosystem evolves.&lt;/p&gt;

&lt;p&gt;The goal is not to create another general-purpose serialization framework. It is to maintain a focused, reusable component whose behavior I can control.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recognizing primitive types during decoding
&lt;/h2&gt;

&lt;p&gt;One of the package's features is its ability to recognize certain primitive types represented as strings.&lt;/p&gt;

&lt;p&gt;The decoder uses regular expressions to identify potential integers, doubles, booleans, and date-time values before attempting their conversion.&lt;/p&gt;

&lt;p&gt;For example, the integer pattern is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;RegExp&lt;/span&gt; &lt;span class="n"&gt;intPattern&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RegExp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sx"&gt;r'^(-|)[0-9]{1,}$'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The corresponding boolean pattern is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;RegExp&lt;/span&gt; &lt;span class="n"&gt;boolPattern&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RegExp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="sx"&gt;r'^(true|false)$'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;caseSensitive:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The decoder then applies the appropriate conversion:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isString&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;datePattern&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hasMatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tryParse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isString&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;boolPattern&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hasMatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tryParse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;caseSensitive:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isString&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;intPattern&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hasMatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tryParse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isString&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;doublePattern&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hasMatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tryParse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows matching values to be converted into their corresponding Dart types.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Input string&lt;/th&gt;
&lt;th&gt;Resulting Dart type&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"true"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bool&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"100"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;int&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"19.95"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;double&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"2026-10-12T15:30:00.000Z"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;DateTime&lt;/code&gt;, if accepted by the date pattern and parser&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"hello"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;String&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The distinction is useful when working with data that has passed through a system that represents values differently from the original application.&lt;/p&gt;

&lt;p&gt;There is an important caveat: &lt;strong&gt;type inference based on string content is a policy, not a universal truth.&lt;/strong&gt; A string containing &lt;code&gt;"100"&lt;/code&gt; might be an identifier rather than an integer. Applications must decide where this conversion is appropriate and where strings must remain unchanged.&lt;/p&gt;

&lt;p&gt;That is why the package also supports explicit transformations.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;ObjectTreatment&lt;/code&gt;: a controlled extension point
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;ObjectTreatment&lt;/code&gt; abstraction allows an application to define how particular objects should be processed.&lt;/p&gt;

&lt;p&gt;Instead of embedding every possible conversion rule in the package itself, the application provides a list of conditions and transformations.&lt;/p&gt;

&lt;p&gt;Conceptually, each treatment contains two callbacks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;test&lt;/code&gt;: determines whether the object matches a particular rule.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;treat&lt;/code&gt;: transforms the matching object into a JSON-compatible representation or another desired Dart value.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The decoding implementation checks these treatments before attempting primitive conversions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;treatments&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ObjectTreatment&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="n"&gt;treatments&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isNotEmpty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;ObjectTreatment&lt;/span&gt; &lt;span class="n"&gt;treatment&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;treatments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;treatment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;treatment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;treat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first matching treatment takes precedence.&lt;/p&gt;

&lt;p&gt;This design separates the conversion mechanism from the application's data policies. The package does not need to understand every database format, domain model, or external API. The application supplies the rules that matter to its own architecture.&lt;/p&gt;

&lt;p&gt;The same concept is available during encoding, allowing custom objects to be transformed before the native JSON encoder processes them.&lt;/p&gt;

&lt;h2&gt;
  
  
  A production example: adapting Dart objects to MongoDB Extended JSON
&lt;/h2&gt;

&lt;p&gt;In Capoerus, one practical use case involves adapting Dart objects to representations supported by MongoDB's Extended JSON conventions.&lt;/p&gt;

&lt;p&gt;Two examples are &lt;code&gt;DateTime&lt;/code&gt; and &lt;code&gt;RegExp&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Encoding &lt;code&gt;DateTime&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;A Dart &lt;code&gt;DateTime&lt;/code&gt; object needs an explicit representation if the receiving system is expected to interpret it as a database date rather than an ordinary string.&lt;/p&gt;

&lt;p&gt;The application defines a treatment that converts the date to UTC and produces a MongoDB-style date document:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;ObjectTreatment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;test:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;object&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;treat:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;object&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toUtc&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;iso8601&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toIso8601String&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;millisecond&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;millisecond&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;millisecondTxt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;millisecond&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;millisecondTxt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;millisecondTxt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;millisecondTxt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;iso8601&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;iso8601&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;replaceFirst&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;millisecondPattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s"&gt;'.&lt;/span&gt;&lt;span class="si"&gt;${millisecondTxt}&lt;/span&gt;&lt;span class="s"&gt;Z'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="si"&gt;$date&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;iso8601&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resulting representation follows the general shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"$date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-12T15:30:00.123Z"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The treatment makes the timezone and millisecond formatting explicit instead of leaving those decisions scattered across the application.&lt;/p&gt;

&lt;p&gt;MongoDB supports several Extended JSON representations, so the exact format should match the interface being used. This example is intended for a compatible date representation, not as a claim that every MongoDB endpoint accepts every Extended JSON variant.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Encoding &lt;code&gt;RegExp&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Regular expressions introduce a different challenge: the pattern itself is not enough. Relevant flags may also need to be preserved.&lt;/p&gt;

&lt;p&gt;The Capoerus implementation transforms a Dart regular expression into a document containing &lt;code&gt;$regex&lt;/code&gt; and, when applicable, &lt;code&gt;$options&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;ObjectTreatment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;test:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;object&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;RegExp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;treat:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;object&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;RegExp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;regex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="si"&gt;$regex&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isDotAll&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;regex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="si"&gt;$options&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s"&gt;'s'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;ifAbsent:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;'s'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isCaseSensitive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;regex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="si"&gt;$options&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s"&gt;'i'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;ifAbsent:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;'i'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isMultiLine&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;regex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="si"&gt;$options&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s"&gt;'m'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;ifAbsent:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;'m'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isUnicode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;regex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="si"&gt;$options&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s"&gt;'u'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;ifAbsent:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;'u'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows the application to preserve selected regular-expression properties when converting the object into a database-compatible representation.&lt;/p&gt;

&lt;p&gt;It also illustrates why a generic JSON encoder alone is not always sufficient for application architecture: the transformation may depend on the expectations of the receiving system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping the database adaptation outside the core package
&lt;/h2&gt;

&lt;p&gt;The conversion package is intentionally independent of MongoDB.&lt;/p&gt;

&lt;p&gt;The database-specific logic belongs to the Capoerus application layer, where the required format and integration behavior are known.&lt;/p&gt;

&lt;p&gt;The application's &lt;code&gt;Json&lt;/code&gt; class provides a convenient interface around the package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'dart:typed_data'&lt;/span&gt; &lt;span class="kd"&gt;show&lt;/span&gt; &lt;span class="n"&gt;Uint8List&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:convert_json/convert_json.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Json&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encoded&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;factory&lt;/span&gt; &lt;span class="n"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;emptyMap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'{}'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;factory&lt;/span&gt; &lt;span class="n"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;emptyList&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'[]'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;factory&lt;/span&gt; &lt;span class="n"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ConvertJson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;factory&lt;/span&gt; &lt;span class="n"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ConvertJson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;encoded&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;dynamic&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="n"&gt;decoded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ConvertJson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;encoded&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;codable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ConvertJson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;codable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;treatments:&lt;/span&gt; &lt;span class="n"&gt;treatmentsForEncoding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The complete production implementation also includes database-specific formatting and decoding helpers, UTF-8 byte conversion, and immutable treatment configuration.&lt;/p&gt;

&lt;p&gt;This separation has an architectural advantage: the package remains focused on conversion, while the application controls how data is prepared for a particular backend.&lt;/p&gt;

&lt;p&gt;It also makes it possible to change database formatting rules without turning the general-purpose conversion layer into a database-specific library.&lt;/p&gt;

&lt;h2&gt;
  
  
  Supporting the reverse transformation
&lt;/h2&gt;

&lt;p&gt;Serialization is only half of the problem. An application must also decide how to interpret the data it receives.&lt;/p&gt;

&lt;p&gt;The Capoerus implementation includes a preprocessing function that recognizes selected MongoDB-style representations before passing the resulting JSON to the decoder.&lt;/p&gt;

&lt;p&gt;For example, a date expressed in milliseconds since the Unix epoch can be converted into an ISO 8601 string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;encoded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;encoded&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;replaceAllMapped&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;dateMillisecondsSinceEpochPattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;dateTxt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="n"&gt;digitsOnlyPattern&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stringMatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;dateEpoch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tryParse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dateTxt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromMillisecondsSinceEpoch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dateEpoch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toUtc&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;'"&lt;/span&gt;&lt;span class="si"&gt;${date.toIso8601String()}&lt;/span&gt;&lt;span class="s"&gt;"'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the preprocessing is complete, the decoder can recognize the resulting date string and attempt to create a Dart &lt;code&gt;DateTime&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is an example of a two-stage conversion pipeline:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Normalize the external representation into ordinary JSON-compatible values.&lt;/li&gt;
&lt;li&gt;Decode those values into Dart objects using the configured conversion rules.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The same general approach can be used for other database-specific structures, provided that the transformation is appropriate for the data format being received.&lt;/p&gt;

&lt;h2&gt;
  
  
  Encoding custom application objects
&lt;/h2&gt;

&lt;p&gt;The package also supports transformations during encoding, including objects that implement a &lt;code&gt;ToJson&lt;/code&gt; interface.&lt;/p&gt;

&lt;p&gt;A simplified view of the encoder's fallback behavior looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toIso8601String&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;ToJson&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;codable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="n"&gt;ConvertJsonException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;'Unsupported object type: &lt;/span&gt;&lt;span class="si"&gt;${object.runtimeType}&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The actual implementation also checks user-defined &lt;code&gt;ObjectTreatment&lt;/code&gt; rules before these fallback cases.&lt;/p&gt;

&lt;p&gt;This gives application developers several options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use the native JSON-compatible types.&lt;/li&gt;
&lt;li&gt;Define custom treatments for specific objects.&lt;/li&gt;
&lt;li&gt;Represent domain objects through &lt;code&gt;ToJson&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Reject unsupported object types rather than silently assuming they can be serialized.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The last point is especially important. Explicit failure can make architectural problems easier to identify than implicit conversions that produce unexpected representations.&lt;/p&gt;

&lt;p&gt;Of course, throwing an exception does not eliminate the need for error handling. Callers must still decide how conversion failures should be reported, logged, or handled.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why keep the package private instead of publishing it on pub.dev?
&lt;/h2&gt;

&lt;p&gt;Not every useful software component needs to become a public package.&lt;/p&gt;

&lt;p&gt;For Capoerus, &lt;code&gt;convert_json&lt;/code&gt; is an internal technology intended to support the ecosystem's implementation requirements.&lt;/p&gt;

&lt;p&gt;Keeping it unpublished on pub.dev allows me to prioritize the needs of the application rather than maintaining a public release cycle or promising compatibility guarantees to external consumers.&lt;/p&gt;

&lt;p&gt;The GitHub repository provides a place to maintain the source and track changes:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Suebersson/convert_json" rel="noopener noreferrer"&gt;https://github.com/Suebersson/convert_json&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This approach is particularly useful when the same conversion rules need to evolve alongside the rest of the application.&lt;/p&gt;

&lt;p&gt;It does not mean that private packages are inherently more secure or reliable than public packages. Those properties depend on implementation quality, testing, maintenance, and the way the software is used.&lt;/p&gt;

&lt;p&gt;The benefit here is control over the component's scope and evolution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pure Dart, without third-party dependencies
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;convert_json&lt;/code&gt; uses Dart's native JSON facilities and adds its own conversion logic on top.&lt;/p&gt;

&lt;p&gt;It is intended for use across the following platforms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Android&lt;/li&gt;
&lt;li&gt;iOS&lt;/li&gt;
&lt;li&gt;Linux&lt;/li&gt;
&lt;li&gt;macOS&lt;/li&gt;
&lt;li&gt;Web&lt;/li&gt;
&lt;li&gt;Windows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because the package does not depend on third-party packages, it avoids introducing additional package-level dependencies into the projects that consume it.&lt;/p&gt;

&lt;p&gt;That does not automatically make the implementation secure, faster, or more reliable. Those qualities still need to be established through testing and appropriate error handling.&lt;/p&gt;

&lt;p&gt;What it does provide is a smaller dependency surface and a conversion layer whose behavior is under direct control.&lt;/p&gt;

&lt;p&gt;The package's scope is also deliberately limited. It is not intended to replace Dart's JSON implementation, provide a complete object-relational mapping system, or guarantee compatibility with every external data format.&lt;/p&gt;

&lt;p&gt;It provides a configurable layer for encoding and decoding data, with additional transformations where the application needs them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I consider important in a conversion layer
&lt;/h2&gt;

&lt;p&gt;When working on an application that spans mobile clients, web interfaces, and server-side components, I find it useful to make data-conversion rules explicit and reusable.&lt;/p&gt;

&lt;p&gt;A conversion layer should have a clearly defined responsibility, predictable failure behavior, and an extension mechanism for application-specific types.&lt;/p&gt;

&lt;p&gt;It should also distinguish between representations that are syntactically valid JSON and representations that carry the intended meaning for a particular application.&lt;/p&gt;

&lt;p&gt;For example, the string &lt;code&gt;"100"&lt;/code&gt; is valid JSON, but that does not tell us whether the application intended a string identifier or an integer. Likewise, a date string can be valid JSON without necessarily matching the representation expected by a database.&lt;/p&gt;

&lt;p&gt;These decisions belong to the architecture, not just to the encoder.&lt;/p&gt;

&lt;p&gt;In Capoerus, &lt;code&gt;convert_json&lt;/code&gt; is one small component of a broader technology ecosystem. Its value comes from being focused, reusable, and adaptable to the requirements of the systems that depend on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;Building reliable software is not always about adopting a large framework or adding another dependency. Sometimes it is about identifying a recurring architectural concern and implementing a focused solution that can be maintained alongside the application.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;convert_json&lt;/code&gt; is my approach to that problem in Dart: a pure-Dart package, available through GitHub, designed for controlled JSON encoding and decoding across the Capoerus ecosystem.&lt;/p&gt;

&lt;p&gt;It is not a universal answer to serialization, and it does not remove the need for validation or error handling. Instead, it provides a place to centralize conversion rules, support custom object transformations, and adapt data to the formats required by the application.&lt;/p&gt;

&lt;p&gt;For me, that is the main architectural benefit: &lt;strong&gt;having control over how data moves between Dart objects, JSON representations, and the systems that consume them.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/Suebersson/convert_json" rel="noopener noreferrer"&gt;https://github.com/Suebersson/convert_json&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;How do you handle custom types and database-specific serialization in your Dart applications? Do you prefer a general-purpose serialization framework, native JSON with custom adapters, or an internal conversion layer?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="mailto:contato@capoerus.com"&gt;Contato&lt;/a&gt; • &lt;a href="https://capoerus.com/" rel="noopener noreferrer"&gt;Website&lt;/a&gt; • &lt;a href="https://capoerus.com/links" rel="noopener noreferrer"&gt;Links&lt;/a&gt; • &lt;a href="https://play.google.com/store/apps/details?id=com.capoerus" rel="noopener noreferrer"&gt;Play Store&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dart</category>
      <category>json</category>
      <category>software</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Dart on Linux + Precompiled C++: Building a Native MongoDB Driver</title>
      <dc:creator>Capoerus</dc:creator>
      <pubDate>Sun, 11 Oct 2026 17:07:13 +0000</pubDate>
      <link>https://dev.to/capoerus/dart-on-linux-precompiled-c-building-a-native-mongodb-driver-14ae</link>
      <guid>https://dev.to/capoerus/dart-on-linux-precompiled-c-building-a-native-mongodb-driver-14ae</guid>
      <description>&lt;p&gt;&lt;a href="https://capoerus.com/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fer8jzdohdbp88w20qz7a.png" alt="Logo" width="800" height="885"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Creating an API in Dart is relatively easy. The challenge begins when the API needs to run reliably in production and depends on secure, well-maintained components for database access, file management, request processing, and cross-platform data handling.&lt;/p&gt;

&lt;p&gt;While developing &lt;a href="https://capoerus.com/" rel="noopener noreferrer"&gt;Capoerus&lt;/a&gt;, I found that ready-made components did not always meet the requirements of my architecture. In some cases, I decided to build my own packages and technologies.&lt;/p&gt;

&lt;p&gt;One of the most interesting engineering decisions was combining &lt;strong&gt;Dart compiled for Linux with a precompiled C++ MongoDB access driver&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This approach provides greater control over implementation, shared data representations, and reduced dependence on third-party packages. However, it also introduces challenges involving native memory management, error handling, and communication between programming languages.&lt;/p&gt;

&lt;p&gt;This article explores how this combination works, why I chose it, and what I learned from the trade-offs.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The Challenge Goes Beyond Creating an API
&lt;/h2&gt;

&lt;p&gt;Dart provides a productive environment for building APIs. However, a production system requires more than routing, request handlers, and asynchronous functions.&lt;/p&gt;

&lt;p&gt;During the development of Capoerus, I needed several components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A universal JSON encoding and decoding package shared by the frontend, backend, and native database driver.&lt;/li&gt;
&lt;li&gt;A MongoDB access driver integrated with Dart.&lt;/li&gt;
&lt;li&gt;Cloud file management for Oracle Cloud Infrastructure Object Storage.&lt;/li&gt;
&lt;li&gt;A lightweight local database that stores data in JSON format on mobile and desktop devices, without requiring code generation.&lt;/li&gt;
&lt;li&gt;A multipart request handler shared between the application and the API.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These components support an architecture in which mobile applications, web applications, and server-side code share a significant portion of their implementation.&lt;/p&gt;

&lt;p&gt;Developing them independently required considerable effort. However, it also allowed me to establish consistent interfaces and behaviors across the entire ecosystem.&lt;/p&gt;

&lt;p&gt;The MongoDB driver is a particularly interesting example because it crosses the boundary between Dart-managed code and native C++ execution.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Why Use C++ for the MongoDB Driver?
&lt;/h2&gt;

&lt;p&gt;The API runs on Linux, compiled from Dart, while the MongoDB driver is implemented in C++ and compiled into a native library before deployment.&lt;/p&gt;

&lt;p&gt;Dart communicates with native functions through FFI (Foreign Function Interface).&lt;/p&gt;

&lt;p&gt;Instead of implementing every database operation entirely in Dart, the driver exposes native functions that can be called through their symbols.&lt;/p&gt;

&lt;p&gt;For example, the Dart side declares an operation like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;findOneSymbol&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'findOne'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@Native&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;assetId:&lt;/span&gt; &lt;span class="n"&gt;assetId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;symbol:&lt;/span&gt; &lt;span class="n"&gt;findOneSymbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;isLeaf:&lt;/span&gt; &lt;span class="n"&gt;isLeaf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;external&lt;/span&gt; &lt;span class="n"&gt;Pointer&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Utf8&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Pointer&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Utf8&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;findOne&lt;/code&gt; function represents a native entry point. Dart passes a pointer to the request, and the native function returns a pointer containing the response.&lt;/p&gt;

&lt;p&gt;Each database operation has its own symbol, allowing operations to be exposed independently.&lt;/p&gt;

&lt;p&gt;This gives the Dart layer a relatively small interface to work with while the native library handles the database-specific implementation.&lt;/p&gt;

&lt;p&gt;It also means that the native library must respect a strict contract regarding memory, data representation, and return values.&lt;/p&gt;

&lt;p&gt;That contract is where the most important engineering decisions begin.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. A Bidirectional Buffer Shared by Dart and C++
&lt;/h2&gt;

&lt;p&gt;The driver uses a buffer allocated on the Dart side for both the request and the response.&lt;/p&gt;

&lt;p&gt;The process works as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Dart serializes the request into JSON.&lt;/li&gt;
&lt;li&gt;Dart encodes the JSON string into UTF-8 bytes.&lt;/li&gt;
&lt;li&gt;Dart allocates a native memory buffer large enough for the request and the expected response.&lt;/li&gt;
&lt;li&gt;The request bytes are copied into that buffer, followed by a null terminator.&lt;/li&gt;
&lt;li&gt;Dart calls the native C++ function, passing the buffer pointer.&lt;/li&gt;
&lt;li&gt;C++ processes the request and writes the response into the same buffer.&lt;/li&gt;
&lt;li&gt;The native function returns the pointer to that buffer.&lt;/li&gt;
&lt;li&gt;Dart reads the response, decodes the JSON, and releases the allocated memory.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Conceptually, the data flow looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dart API
   |
   | Serialize request to JSON
   | Encode as UTF-8
   | Allocate response-capable buffer
   v
+------------------------------+
| Dart-allocated native buffer |
|                              |
| JSON request                 |
+------------------------------+
   |
   | FFI call
   v
+------------------------------+
| Precompiled C++ driver       |
|                              |
| Process request              |
| Execute MongoDB operation    |
| Write response into buffer   |
+------------------------------+
   |
   | Return the same pointer
   v
Dart API
   |
   | Read UTF-8 response
   | Decode JSON
   | Release allocation
   v
Application response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key idea is that the native function does not need to allocate a separate response buffer and transfer ownership of that allocation to Dart.&lt;/p&gt;

&lt;p&gt;Instead, &lt;strong&gt;Dart owns the allocation, and C++ writes the response into the memory provided by Dart&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This design reduces the complexity of memory ownership between the two layers. However, it does not automatically provide memory safety. The native implementation must respect the buffer's capacity and the agreed pointer contract.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Allocating Enough Memory Is the Difficult Part
&lt;/h2&gt;

&lt;p&gt;The buffer must be large enough to contain the response, not merely the original request.&lt;/p&gt;

&lt;p&gt;A small MongoDB query can return a large document. A query that returns multiple documents can produce a response considerably larger than its input.&lt;/p&gt;

&lt;p&gt;If C++ writes beyond the allocated capacity, it can corrupt memory and crash the process.&lt;/p&gt;

&lt;p&gt;For a server-side API, this is especially important because a native crash can terminate the entire process rather than just fail a single request.&lt;/p&gt;

&lt;p&gt;The Dart implementation therefore accepts a response size limit and checks it against the encoded request size before allocating memory.&lt;/p&gt;

&lt;p&gt;A simplified version of the validation is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Uint8List&lt;/span&gt; &lt;span class="n"&gt;requestBytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;utf8&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toJson&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;requestBytesLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requestBytes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;responseSizeLimit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;responseSizeLimit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;responseSizeLimit&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;requestBytesLength&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MongodbException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;'The response size limit cannot be smaller '&lt;/span&gt;
    &lt;span class="s"&gt;'than the request size.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The actual implementation also reserves one additional byte for the null terminator.&lt;/p&gt;

&lt;p&gt;This initial validation prevents an obviously undersized buffer from being passed to C++. However, it does not guarantee that the response will fit.&lt;/p&gt;

&lt;p&gt;The native side must enforce the capacity before every write, and the driver must define what happens when a response exceeds the configured limit.&lt;/p&gt;

&lt;p&gt;A robust implementation should reject oversized responses or return a controlled error rather than attempting to write beyond the buffer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why the Null Terminator Matters
&lt;/h3&gt;

&lt;p&gt;The native function receives a pointer to UTF-8 data represented as a null-terminated string.&lt;/p&gt;

&lt;p&gt;The allocation therefore includes an additional byte:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Pointer&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Uint8&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;allocatedPointer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Uint8&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;responseSizeLimit&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The request is then copied into the allocated memory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Uint8List&lt;/span&gt; &lt;span class="n"&gt;nativeBytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;allocatedPointer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;asTypedList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requestBytesLength&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;nativeBytes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;requestBytes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;nativeBytes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;requestBytesLength&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The zero byte marks the end of the C-style string.&lt;/p&gt;

&lt;p&gt;When C++ writes the response, it must preserve that convention and ensure that the response and its terminator fit within the available memory.&lt;/p&gt;

&lt;p&gt;The response must also be valid UTF-8 JSON before Dart attempts to decode it.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Keeping the Pointer Contract Explicit
&lt;/h2&gt;

&lt;p&gt;Another important check verifies that the native function returns the same pointer that Dart originally allocated.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;allocatedPointerAddress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;allocatedPointer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Pointer&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Utf8&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;requestPointer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;allocatedPointer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Utf8&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Pointer&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Utf8&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;responsePointer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requestPointer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;allocatedPointerAddress&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;responsePointer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MongodbException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;'The native function returned an unexpected pointer.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a simplified illustration of the pointer validation used by the driver.&lt;/p&gt;

&lt;p&gt;The native functions are expected to write the response into the original allocation and return that same address.&lt;/p&gt;

&lt;p&gt;This makes the ownership contract explicit: the native function must not return an independently allocated response buffer under the same interface.&lt;/p&gt;

&lt;p&gt;One limitation is worth noting: checking the address after the native function returns cannot prevent an invalid write that has already occurred. Pointer validation is a contract check, not a substitute for bounds checking inside C++.&lt;/p&gt;

&lt;p&gt;The native implementation must enforce the buffer capacity before writing any response bytes.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Dart Manages the Allocation, but the Trade-Off Remains
&lt;/h2&gt;

&lt;p&gt;One benefit of this design is that Dart controls the buffer's allocation and release.&lt;/p&gt;

&lt;p&gt;C++ does not need to transfer ownership of a separately allocated response buffer to the Dart layer. This avoids one class of cross-language ownership problems.&lt;/p&gt;

&lt;p&gt;The Dart implementation can read the response and then release the allocation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;responsePointer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toDartString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Json&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;treaty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requestPointer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The example illustrates the intended lifecycle, but production code must handle cleanup on every execution path.&lt;/p&gt;

&lt;p&gt;For example, if decoding throws an exception before &lt;code&gt;malloc.free&lt;/code&gt; executes, the allocation could leak. A safer implementation places the native allocation in a &lt;code&gt;try/finally&lt;/code&gt; lifecycle so that the buffer is released even when an operation fails.&lt;/p&gt;

&lt;p&gt;The central trade-off remains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dart controls allocation and release.&lt;/li&gt;
&lt;li&gt;C++ must respect the memory boundaries established by Dart.&lt;/li&gt;
&lt;li&gt;A native buffer overflow can still crash the API.&lt;/li&gt;
&lt;li&gt;Incorrect pointer handling can still cause undefined behavior.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is one of the areas where native integration requires more care than an ordinary Dart function call.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Exceptions and Controlled Failures
&lt;/h2&gt;

&lt;p&gt;A database driver should not assume that every operation succeeds.&lt;/p&gt;

&lt;p&gt;MongoDB operations can fail, JSON responses can be invalid, and unexpected conditions can occur at the native boundary.&lt;/p&gt;

&lt;p&gt;The driver therefore catches its own database-related exceptions and unexpected Dart-side exceptions, records diagnostic information, and can return an alternative JSON response.&lt;/p&gt;

&lt;p&gt;The public interface includes an optional &lt;code&gt;ifFail&lt;/code&gt; callback:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Json&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Json&lt;/span&gt; &lt;span class="kt"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;ifFail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Request processing and native invocation.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The callback allows the caller to supply alternative data when a request fails.&lt;/p&gt;

&lt;p&gt;The general fallback behavior is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="n"&gt;ifFail&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;call&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="n"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;emptyMap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives the calling code a way to handle failures without necessarily propagating every exception through the entire request-handling stack.&lt;/p&gt;

&lt;p&gt;However, exception handling has an important boundary.&lt;/p&gt;

&lt;p&gt;Catching Dart exceptions does not reliably recover from arbitrary native crashes, segmentation faults, or memory corruption. Those failures may terminate the process before Dart can execute its catch block.&lt;/p&gt;

&lt;p&gt;For this reason, exception handling must complement, rather than replace, native validation and memory safety.&lt;/p&gt;

&lt;p&gt;In addition, a fallback value should not make a failed database operation indistinguishable from a successful one. The application must preserve enough information to determine whether the requested operation actually succeeded.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Why Precompiled Native Code Requires a Security Strategy
&lt;/h2&gt;

&lt;p&gt;Using a native library also changes the security considerations.&lt;/p&gt;

&lt;p&gt;A precompiled library does not expose its implementation as readable source code in the deployed artifact. That can make its behavior harder to inspect during routine debugging, especially when debugging symbols and build artifacts are unavailable.&lt;/p&gt;

&lt;p&gt;But compiled code is not inherently trustworthy, nor is it impossible to analyze.&lt;/p&gt;

&lt;p&gt;A native library can contain vulnerabilities or perform unintended operations regardless of whether its source code is visible.&lt;/p&gt;

&lt;p&gt;For a component that has access to a database and runs inside an API process, I consider control over the implementation an important advantage.&lt;/p&gt;

&lt;p&gt;Developing the driver internally gives me control over its source code, its build process, its exposed functions, and the changes introduced between versions.&lt;/p&gt;

&lt;p&gt;It also allows the interface between Dart and C++ to remain narrow and explicit.&lt;/p&gt;

&lt;p&gt;Still, these benefits should not be confused with an automatic security guarantee.&lt;/p&gt;

&lt;p&gt;A custom driver needs code review, dependency management, controlled builds, tests, and careful validation of native inputs. The same applies to permissions and access credentials: the native library should only receive the capabilities it actually needs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Owning the implementation improves control; security still depends on how that implementation is built, reviewed, and operated.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  9. One JSON Language Across the Ecosystem
&lt;/h2&gt;

&lt;p&gt;The native MongoDB driver is only one part of the architecture.&lt;/p&gt;

&lt;p&gt;Another important component is the JSON encoding and decoding package developed for Capoerus.&lt;/p&gt;

&lt;p&gt;The same representation is used across the mobile application, backend, and native driver.&lt;/p&gt;

&lt;p&gt;This reduces the number of translation layers required between components.&lt;/p&gt;

&lt;p&gt;Instead of maintaining different data representations for each platform, the system uses a shared JSON contract.&lt;/p&gt;

&lt;p&gt;The result is a more consistent flow of data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Mobile application
       |
       | JSON
       v
Dart application and API
       |
       | JSON encoded as UTF-8
       v
Native C++ MongoDB driver
       |
       | JSON response
       v
Dart API and application
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;JSON does not eliminate the need for validation or versioning. A shared format still requires consistent handling of missing fields, data types, invalid responses, and schema changes.&lt;/p&gt;

&lt;p&gt;Its value is that the components share a common representation and can evolve around the same conventions.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. The Benefits Extend Beyond MongoDB
&lt;/h2&gt;

&lt;p&gt;Developing the native driver makes more sense when considered alongside the other components created for the ecosystem.&lt;/p&gt;

&lt;p&gt;Capoerus also uses its own solutions for local JSON-based storage, cloud file management with Oracle Cloud Infrastructure Object Storage, JSON serialization, and multipart request handling.&lt;/p&gt;

&lt;p&gt;These components address different problems, but they follow a common architectural principle: establish interfaces that work consistently across the applications and services that need them.&lt;/p&gt;

&lt;p&gt;This approach has practical advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Consistency:&lt;/strong&gt; mobile, web, and server components share common conventions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Autonomy:&lt;/strong&gt; implementation decisions are not entirely dependent on external package maintainers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adaptability:&lt;/strong&gt; changes can be coordinated across the ecosystem.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduced dependency surface:&lt;/strong&gt; fewer third-party components are required for the functionality implemented internally.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Control over updates:&lt;/strong&gt; internal components can evolve according to the project's needs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these advantages means that building everything in-house is automatically better.&lt;/p&gt;

&lt;p&gt;Maintaining custom infrastructure creates responsibilities that would otherwise belong to package maintainers. Security updates, compatibility, testing, documentation, and debugging become part of the project's own workload.&lt;/p&gt;

&lt;p&gt;The benefits must justify that ongoing cost.&lt;/p&gt;

&lt;h2&gt;
  
  
  11. The Cost of Building Your Own Technology
&lt;/h2&gt;

&lt;p&gt;There is a downside to this approach that is easy to underestimate.&lt;/p&gt;

&lt;p&gt;Creating a package is only the beginning.&lt;/p&gt;

&lt;p&gt;It must be tested, documented, maintained, and adapted as the application evolves. Native code adds another layer of complexity because failures can occur outside Dart's managed execution environment.&lt;/p&gt;

&lt;p&gt;A third-party package with a mature implementation may be a better choice for many projects.&lt;/p&gt;

&lt;p&gt;For Capoerus, however, developing these components has provided a high degree of autonomy and a consistent technical foundation across the ecosystem.&lt;/p&gt;

&lt;p&gt;The decision was not simply to avoid dependencies. It was to build components whose behavior and interfaces could be coordinated with the rest of the architecture.&lt;/p&gt;

&lt;p&gt;That distinction matters.&lt;/p&gt;

&lt;p&gt;Reducing dependencies is useful only when the cost of owning the replacement remains manageable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Combining Dart compiled for Linux with a precompiled C++ MongoDB driver has allowed Capoerus to establish a direct interface between its API and native database operations.&lt;/p&gt;

&lt;p&gt;The design uses a Dart-allocated buffer for both request and response data, making memory ownership easier to define. It also creates strict requirements for buffer capacity, pointer identity, null termination, exception handling, and cleanup.&lt;/p&gt;

&lt;p&gt;The larger benefit comes from how this component fits into the rest of the system.&lt;/p&gt;

&lt;p&gt;A shared JSON representation, custom local storage, cloud file management, and reusable request-processing components help keep mobile applications, web applications, and server-side code aligned.&lt;/p&gt;

&lt;p&gt;The price is substantial engineering and maintenance work. The reward is greater control over implementation, fewer unnecessary translation layers, and the freedom to adapt components to the needs of the project.&lt;/p&gt;

&lt;p&gt;For me, the most important lesson is not that native C++ is always the right choice for a Dart backend.&lt;/p&gt;

&lt;p&gt;It is that &lt;strong&gt;the boundary between two languages can become a useful architectural tool when its ownership rules, data contract, and failure modes are designed explicitly&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That boundary is also where the most careful engineering is required.&lt;/p&gt;

&lt;p&gt;&lt;a href="mailto:contato@capoerus.com"&gt;Contato&lt;/a&gt; • &lt;a href="https://capoerus.com/" rel="noopener noreferrer"&gt;Website&lt;/a&gt; • &lt;a href="https://capoerus.com/links" rel="noopener noreferrer"&gt;Links&lt;/a&gt; • &lt;a href="https://play.google.com/store/apps/details?id=com.capoerus" rel="noopener noreferrer"&gt;Play Store&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dart</category>
      <category>cpp</category>
      <category>backend</category>
      <category>mongodb</category>
    </item>
  </channel>
</rss>
