<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by David Sousa on Medium]]></title>
        <description><![CDATA[Stories by David Sousa on Medium]]></description>
        <link>https://medium.com/@davidbdsousa?source=rss-5ca05a011bf------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/0*3-rM7YkZ93pf-RT8.jpg</url>
            <title>Stories by David Sousa on Medium</title>
            <link>https://medium.com/@davidbdsousa?source=rss-5ca05a011bf------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 10:09:09 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@davidbdsousa/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Learning NoSQL the easy way]]></title>
            <link>https://blog.product-league.com/learning-nosql-the-easy-way-807493d1262c?source=rss-5ca05a011bf------2</link>
            <guid isPermaLink="false">https://medium.com/p/807493d1262c</guid>
            <category><![CDATA[outsystems]]></category>
            <category><![CDATA[nosql]]></category>
            <category><![CDATA[mongodb]]></category>
            <dc:creator><![CDATA[David Sousa]]></dc:creator>
            <pubDate>Mon, 16 Mar 2020 11:56:35 GMT</pubDate>
            <atom:updated>2020-03-16T13:41:10.527Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="OutSystems + MongoDB" src="https://cdn-images-1.medium.com/max/1024/1*SbjaCf7E5fs2_6B2j41Bpg.png" /></figure><p>Disclaimer: This is about databases, run while you still can!</p><h3>Intro</h3><p>In one of our recent projects we, at Product League, were asked to build a messaging system. This chat would have to include almost all the features that known applications such as <em>WhatsApp </em>offer nowadays, such as:</p><ul><li>Real time messaging;</li><li>Support both mobile and web (with no mobile backup — in your face <em>WhatsApp</em>!);</li><li>1-on-1 and group/channel conversations;</li><li>Sending and downloading attachments;</li><li>Read receipts (the little tick marks that tell you if a message is sent, received or read).</li></ul><p>While we were analyzing the requirements for this <a href="https://en.wikipedia.org/wiki/Minimum_viable_product">MVP</a>, we identified the data model as a potential growth bottleneck. The reference architectures for this type of applications adopt <a href="https://en.wikipedia.org/wiki/NoSQL"><strong>NoSQL databases</strong></a> to support their data storage. But since we were in the MVP phase, where the time is of the essence and the level of adoption of the app was still unclear, it was strategically decided to focus on achieving other requirements and leave the NoSQL data model on the backlog for future iterations.</p><p>Since we’re using OutSystems, we decided in the meantime to leverage its power and build the chat on top of the included SQL Server database.</p><p>For the sake of simplicity, the data model looked like this:</p><ul><li><strong><em>Conversation </em></strong>and <strong><em>ConversationUser </em></strong>tables, to store the high level information about a conversation and its related users. Data growth won’t be a problem since these tables will be mostly used for reads;</li><li><strong><em>ConversationMessage</em></strong>, where messages are stored. The amount of information grows linearly with the number of messages sent in a conversation;</li><li><strong><em>ConversationMessageUser</em></strong>, storing the status (sent, received, read, etc…) of each message and each user. In this case, the amount of entries is expected to grow exponentially since it depends on the amount of users in a conversation.</li></ul><figure><img alt="Simplified relational data model of the messaging system" src="https://cdn-images-1.medium.com/max/670/1*bC7k1ZulUMJsiBthnLNboQ.png" /><figcaption>Simplified relational data model of the messaging system</figcaption></figure><h3>The challenge</h3><p>We were quite happy with our result since the application was successfully rolled to production. The adoption was great and the client decided to invest further in the concept. Besides all new requirements and changes due to the close feedback loop between the Users and the Team, we knew that it was time to give priority to the NoSQL entry in the backlog.</p><p>The first thing we did was to analyze the implications of the new expected user base, which was predicted to grow over 22000 users.</p><p>We noticed that, even with a low usage of the chat (around 5 messages sent per day, per user), the <strong><em>ConversationMessageUser </em></strong>table, that stores the read receipts, would grow on average 1GB per day. Now imagine groups with over 200 users, in which a single message would generate the same amount of entries!</p><p>We knew that, sooner or later, <strong>performance would be affected by the data volume growth</strong>.</p><h3>Now what?</h3><p>We started to study possible <strong>NoSQL database engines </strong>that would support this data growth while keeping the desired performance.</p><p>Since we were already using Azure Cloud services in other features of the application, we took a glance at the CosmosDB service, which offers NoSQL solutions. Among the different options we chose the <a href="https://docs.microsoft.com/en-us/azure/cosmos-db/mongodb-introduction"><strong>MongoDB API</strong></a>, since it’s a widely adopted database in most of the similar investigated scenarios. The growing community of Mongo also played a big role in this selection. The integration with it was straightforward since there was already a component for <a href="https://www.outsystems.com/forge/component-overview/1904/mongodb">Mongo integration</a> in the OutSystems Forge.</p><p>Our MongoDB knowledge was limited, therefore we knew that a “fail fast” approach to this data model change was the way to move forward.</p><h4>First steps</h4><p>Our initial approach was to have one MongoDB collection and store each conversation as a document. Everything related to a conversation — users, messages and respective read receipts — would be nested inside a single document. This meant that:</p><ul><li>Each action performed by a user (e.g. writing, receiving or reading a message) would update the conversation document;</li><li>Each document would grow indefinitely as the number of messages also grew.</li></ul><p>At the same time, we quickly became aware of the CosmosDB pricing model, the <a href="https://docs.microsoft.com/en-us/azure/cosmos-db/request-units"><strong>Request Units</strong></a>.<strong> </strong>We’ll call them RU from now on.</p><p>RU is a normalized measure that represents the cost of all database operations, abstracting systems resources such as CPU and memory. It defines the maximum throughput (every <a href="https://en.wikipedia.org/wiki/Create,_read,_update_and_delete">CRUD</a> action uses an amount of RU) and, most importantly, the monthly cost.</p><p>For example, a basic limit of 400 RU/second represents a cost of 23$/month.</p><p>This throughput is set when the MongoDB collection is created and can be changed both in Azure and programatically. This way, a smaller value can be set initially, when the usage is lower, and increased over time when deemed necessary.</p><p>After some tests, we quickly concluded that this first iteration would become very expensive for our client. Each conversation document was growing quickly and soon we were hitting the 400 RU/s limit with all the updates performed over each document. This data model allowed us to conclude that:</p><ul><li>We were thinking too much in a relational way. After all, each document looked almost as its SQL equivalent;</li><li>Too many updates on such a big document, with so many nested fields, was consuming a lot of RU.</li></ul><p>For all this we knew that we had to improve our approach to NoSQL.</p><h4>Climbing the ladder</h4><p>The second iteration made us do a 180-degree turn over our initial approach. The idea was to keep the documents smaller, with less nested fields, and to try to stay away from the costly updates. The concept looked like this:</p><ul><li>Each document represented a message from a user’s perspective. For example, in a 1-on-1 conversation, each message was represented by 2 documents, one for each user;</li><li>In any action over a message (e.g. user receiving or reading a message) we would fetch the latest message document, update the necessary fields in the back-end and insert the new document in the database;</li><li>This meant that a message, over time, would be represented by lots of documents — snapshots of the message status in any moment of time. This isn’t a problem since, under the NoSQL paradigm, data replication is a common practice;</li><li>Since the documents had a timestamp, it allowed us to easily identify the latest state of a message.</li></ul><p>With the second iteration we also took advantage of partitions (a.k.a. <a href="https://docs.mongodb.com/manual/sharding/">shards</a>), which is how CosmosDB handles the database scaling in order to meet good performance levels. A well defined partition key allows CosmosDB to efficiently spread the load over different servers. A couple of <strong>rules of thumb when using partitions</strong> are:</p><ul><li>It’s important to define a partition key that is evenly distributed across all the documents. For example, if we’re to use the <em>conversation identifier</em> as the partition key and almost all the messages exchanged in the app were sent in that particular conversation, the load would be focused on that partition. As we want to avoid that, a good partition key should ensure that the partitions are evenly used when inserting documents.</li><li>When retrieving documents, it’s good practice to use the partition key as the first filter condition. This way, CosmosDB only has to access a single partition, thus using less RU and being way faster. Cross partition queries are possible, but are slower and costlier.</li></ul><p>We ended up choosing the <strong><em>user identifier </em>as the partition key</strong>, which seemed a reasonable approach since the same app usage is expected from most of our users.</p><p>On our second concept’s initial tests we saw a decrease on the RU usage and a better performance overall. But, in some cases, the data was not consistent due to concurrent receiving or reading of messages.</p><p>Since we were retrieving the latest snapshot, editing it and creating a new one, if two users receive or read a message at the same time, the following scenario happened:</p><ul><li>Users A and B retrieve latest snapshot (in each retrieved document no one has read the message);</li><li>User A creates a snapshot where only he has read the message;</li><li>User B creates a snapshot where only he has read the message, when in fact the created snapshot should say that both users had read it.</li></ul><p>We evaluated the possibility of using a message queue, which could help us solve the concurrency issues, but in the end we felt that we needed to improve the data model further.</p><p>That’s how we reached the last iteration, in which we aimed to <strong>fix the concurrency problem</strong> and <strong>improve even more the RU consumption</strong>.</p><h4>Final step</h4><p>We ended up keeping most of the previous concept but decided to store the messages and the read receipts separately, using two collections:</p><ul><li>The <strong>messages collection</strong> — where we stored each message per user and only once — when the message was written;</li><li>The <strong>read receipts collection</strong> — each time a user receives or reads a message, a new document is inserted with the latest status. This way, each user is responsible for creating documents that only concern his actions, avoiding the concurrency issues of the previous approach.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/426/1*n1ol1weKCVNxsch53d9_DA.png" /><figcaption>Representation of the message and read receipt documents</figcaption></figure><p>The same partition key was kept, meaning that <strong>each user would have its own representation of a message and read receipts</strong>.</p><p>We also tried to <strong>keep the documents as plain as possible</strong> (avoiding nested fields) in order to keep a low RU usage.</p><p>After running a lot of tests, this version was working well in all scenarios and was considered stable. It allowed simpler queries, which helped bring down the RU consumption overall.</p><h3>What next?</h3><p>We’re now eager to roll the solution to production, but taking in account the following considerations:</p><ul><li>The MongoDB feature will initially live in parallel with the existing SQL solution. It can be toggled on and off without affecting the user experience;</li><li>A/B testing can be applied to just a few conversations, ensuring that we have a way to control and adjust the RU limit to a real scenario. It will allow us to roll out the change progressively to all the users;</li><li>With the data migration from SQL to NoSQL we expect a higher RU consumption. Fortunately, CosmosDB allows you to adjust the RU/s limit when needed.</li></ul><h3>Final notes</h3><p>This was an exhilarating learning journey, from which the key takeaways are:</p><ul><li>The MVP approach is the best way to promote adoption and evolution;</li><li>Failing fast allowed us to improve our knowledge on non-relational databases and quickly iterate in order to gradually enhance our MongoDB implementation.</li></ul><p>Wrapping up, we adopted NoSQL and reached a great solution! Nonetheless, we’ll continue to look for improvements and to learn from other’s experiences.</p><p><strong>References</strong></p><p>CosmosDB request units:</p><ul><li><a href="https://docs.microsoft.com/en-us/azure/cosmos-db/request-units">https://docs.microsoft.com/en-us/azure/cosmos-db/request-units</a></li></ul><p>CosmosDB partitions:</p><ul><li><a href="https://docs.microsoft.com/en-us/azure/cosmos-db/partitioning-overview">https://docs.microsoft.com/en-us/azure/cosmos-db/partitioning-overview</a></li><li><a href="https://azure.microsoft.com/en-us/resources/videos/azure-documentdb-elastic-scale-partitioning/">https://azure.microsoft.com/en-us/resources/videos/azure-documentdb-elastic-scale-partitioning/</a></li></ul><p>A good IDE to explore MongoDB documents:</p><ul><li><a href="https://studio3t.com/">https://studio3t.com/</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=807493d1262c" width="1" height="1" alt=""><hr><p><a href="https://blog.product-league.com/learning-nosql-the-easy-way-807493d1262c">Learning NoSQL the easy way</a> was originally published in <a href="https://blog.product-league.com/">ProductLeague</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Firebase for your OutSystems mobile app]]></title>
            <link>https://medium.com/truewind/firebase-for-your-outsystems-mobile-app-f5cfd69912f7?source=rss-5ca05a011bf------2</link>
            <guid isPermaLink="false">https://medium.com/p/f5cfd69912f7</guid>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[firebase]]></category>
            <category><![CDATA[outsystems]]></category>
            <category><![CDATA[android]]></category>
            <dc:creator><![CDATA[David Sousa]]></dc:creator>
            <pubDate>Fri, 28 Dec 2018 19:53:22 GMT</pubDate>
            <atom:updated>2019-02-11T15:45:01.313Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*WeuC_H_Vrs9H8XF-ownaHw.png" /></figure><h4>Why Firebase?</h4><p>I’m sure everyone who builds a mobile app will want to know their users, what they do and how much time they spend on it.</p><p>Having this kind of information will allow you to make better decisions in order to expand your user base and to keep them happy with the app. Acquiring it, however, can be a hard task.</p><p>That’s when Firebase comes to the rescue. It was built with mobile apps in mind and comes packed with many products that provide us with ways to get to know our users.</p><h4>Firebase features</h4><p>Firebase comes with an <strong>Analytics </strong>tool which gathers information on the application usage and user engagement, through <strong>event reporting</strong> and <strong>user properties</strong>. It gives us insights that go from user acquisition to app interaction and user retention. Some cool features are:</p><ul><li>Some events are automatically captured — such as app installation, app start or app removal. It also allows us to define our own custom events, with optional event parameters, to measure what fits our business needs, and use them on button clicks, screen views, etc.</li><li>User properties can be used to define audiences or user segments. These segments can be later on targeted with notifications or specific app configurations. We could, for example, pick only the users that are inactive for a period of time and send them a push notification to get them back in the app.</li></ul><p>Firebase also comes with other useful products such as:</p><ul><li><strong>Crashlytics</strong>, a crash reporting tool, which gives us a detailed report on the app crashes and how many users it affected.</li><li><strong>Performance monitoring</strong>, helping us to track down possible performance issues. It allows us to measure key interactions (such as login, data synchronization, etc) and to check how long they take on a variety of different conditions, such as app version, mobile phone brand, type of internet connection, operating system and so on.</li><li><strong>Cloud messaging</strong>, giving us the possibility to define and send push notifications to users.</li><li><strong>Remote configuration</strong>, which allows us to define configurations and apply them to a set of users. For example, we could target 10% of our user base and have them experience a new app theme. Joined with A/B testing, audiences and analytics, it proves to be a powerful tool to test the efficiency of new features on a subset of your user base.</li></ul><p>This all sounds great, but what’s even better is that its base plan is <a href="https://firebase.google.com/pricing/"><strong>free</strong></a>!</p><p>Now, an important question... How can I integrate my OutSystems mobile app with Firebase?</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/622/1*D0eLO8w6eidRfjUKSfnFAg.jpeg" /></figure><h4>Our component</h4><p>Well, we built the <a href="https://www.outsystems.com/forge/component-overview/4991/firebase-mobile">Firebase Mobile</a> component and published it in the OutSystems Forge! It provides the integration with Firebase for both Android and iOS operating systems, while also including a server-side module responsible for sending push notifications through Firebase’s API.</p><p>We also added to the Forge a sample application showing some of our plugin’s features and how to use them. You can check it <a href="https://www.outsystems.com/forge/component-overview/4999/">here</a>.</p><p>Configuring Firebase may seem the hardest part, so that’s why I’m going to only focus on that for now. Feel free to follow this guide and configure the sample app with your own Firebase project in your OutSystems environment.</p><h4>How to configure</h4><p>So, first of all, go to the <a href="https://console.firebase.google.com/">Firebase console</a> and create a project if you don’t have one already.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*T_4hOhXIWgd32L8wOWN_Bg.png" /><figcaption>Creating a Firebase project</figcaption></figure><p>Add a new Firebase app for each mobile operating system your app supports. Since OutSystems allows us to build native apps for Android and iOS, let’s add both. While adding each Firebase app, use the app identifier obtainable from Service Studio.</p><p>In the Firebase console, open the Android app wizard, register the app in the first step and download the configuration file in the second step. Keep the file — <strong>google-services.json </strong>— since we’ll be using it next. Skip the remaining steps.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/645/1*y956XaQ8S_MPx9iwak3pzA.png" /><figcaption>Registering the Android app in Firebase</figcaption></figure><p>Similarly, register the iOS app and download the configuration file — <strong>GoogleService-Info.plist</strong>. Also, safely skip the remaining steps.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/652/1*IlYu8MT9Kj0ZGg5QODSO_g.png" /><figcaption>Registering the iOS app in Firebase</figcaption></figure><p>Now that you have both configuration files, let’s prepare them so that we can add them to the OutSystems app.</p><p>Add both <strong>google-services.json</strong> and <strong>GoogleService-Info.plist</strong> to a zip folder. Ensure that the folder is called <strong>google-services.zip</strong> and that it only contains both config files.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/949/1*byV9KkjeT-64sdVaoalCBw.png" /><figcaption>Creating a zip folder with both configuration files</figcaption></figure><p>Finally, open your app module in Service Studio and add the zip folder in the Resources tab. Make sure that:</p><ul><li>The<strong> Name </strong>is “google-services.zip”</li><li>The<strong> Deploy action</strong> is “deploy to target directory”</li><li>The<strong> Target directory</strong> is the concatenation of “firebase.” with the app identifier. So, if your app identifier looks something like “com.example.myapp”, the target directory should be “firebase.com.example.myapp”.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/747/1*9jhHhEQ8YaAdHeHe9HREUA.png" /><figcaption>Configuration files added in Service Studio</figcaption></figure><p>With the configuration done, you can now build the application and use Firebase Mobile’s actions in your code. When you install the app and open it, it’ll be successfully registered with Firebase.</p><h4>Support to multiple environments</h4><p>If you’re wondering why we have to configure the “target directory” of the configuration files to include the application identifier, that’s because we need to support having Firebase in a multiple environment scenario.</p><p>For example, if we have 3 environments (DEV, QA and PROD), and a different app identifier in each environment, we’ll also end up with a Firebase project for each environment.</p><p>In this case we’ll need to create 3 zip folders with the respective configuration files and add them in the Resources tab in the lowest environment (DEV).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/739/1*aHIagpTzHtoT3Kvx14VSUw.png" /><figcaption>Enabling Firebase in multiple environments</figcaption></figure><p>Afterwards, we can safely deploy our application to the upper environments with Lifetime.</p><p>The plugin will pick the correct zip folder when the app is being built and apply the right configurations to the app.</p><h4>Final notes</h4><p>It’s important to note that Firebase is constantly being improved and receiving new features. Also, the mobile ecosystem is an ever-changing world, with new OS versions almost every year. Our plugin will also continue to adapt to all the external changes.</p><p>If the article aroused your interest in this amazing tool, please visit the <a href="https://www.outsystems.com/forge/component-overview/4991/">Firebase Mobile</a> plugin page, which also includes some useful documentation, and the <a href="https://www.outsystems.com/forge/component-overview/4999/">Firebase Mobile Sample</a>, where you can test some features and take a look at the code.</p><p>If you have any doubt, feel free to ask a question in the forums.</p><p>To wrap it up, I would like to thank my <a href="https://www.truewindglobal.com/">Truewind team</a>, the <a href="https://github.com/arnesson/cordova-plugin-firebase">github repo</a> which served as a base to our plugin, and finally you for reading the article!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f5cfd69912f7" width="1" height="1" alt=""><hr><p><a href="https://medium.com/truewind/firebase-for-your-outsystems-mobile-app-f5cfd69912f7">Firebase for your OutSystems mobile app</a> was originally published in <a href="https://medium.com/truewind">Truewind</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>