<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Architecture &#8211; Zhijun Chen</title>
	<atom:link href="https://zhijunchen.com/category/architecture/feed/" rel="self" type="application/rss+xml" />
	<link>https://zhijunchen.com</link>
	<description></description>
	<lastBuildDate>Fri, 07 Jun 2024 06:29:46 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.5.4</generator>

<image>
	<url>https://zhijunchen.com/wp-content/uploads/2021/03/cropped-my-site-icon-1-32x32.png</url>
	<title>Architecture &#8211; Zhijun Chen</title>
	<link>https://zhijunchen.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Using CircleCI, Expo EAS, BrowserStack to build React Native Application Deployment Pipeline</title>
		<link>https://zhijunchen.com/using-circleci-expo-eas-browserstack-to-build-react-native-application-deployment-pipeline/</link>
		
		<dc:creator><![CDATA[Zhijun Chen]]></dc:creator>
		<pubDate>Tue, 16 Aug 2022 08:32:47 +0000</pubDate>
				<category><![CDATA[Architecture]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[BrowserStack]]></category>
		<category><![CDATA[CircleCI]]></category>
		<category><![CDATA[Expo]]></category>
		<category><![CDATA[React Native]]></category>
		<guid isPermaLink="false">https://zhijunchen.com/?p=186</guid>

					<description><![CDATA[I recently got a chance to work within a Frontend team which delivers iOS and Android applications to our customers. One of the pain point in the team was the difficulty in app testing and release so I spent some time on investigating the best approach for the team. The team is using React Native [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p style="font-size:18px">I recently got a chance to work within a Frontend team which delivers iOS and Android applications to our customers. One of the pain point in the team was the difficulty in app testing and release so I spent some time on investigating the best approach for the team. The team is using <a rel="noreferrer noopener" href="https://reactnative.dev/" target="_blank">React Native</a> based <a rel="noreferrer noopener" href="https://expo.dev/" target="_blank">Expo</a> and at this stage highly replying on manual testing on physical devices. They are currently putting efforts on automated testing based on <a rel="noreferrer noopener" href="https://wix.github.io/Detox/" target="_blank">Detox</a>.</p>



<h2 class="wp-block-heading">Expo EAS</h2>



<p style="font-size:18px">Expo provides a React Native build tool called <a rel="noreferrer noopener" href="https://expo.dev/eas" target="_blank">Expo Application Services (EAS)</a>, it allows you to build and submit applications easily. By default when you run <code>eas build</code>, it will create a build on their cloud services and you will have to pay for priority pipeline execution. The Free plan at the time of writing only allows you to build one pipeline at a time. In our case we already have <a rel="noreferrer noopener" href="https://circleci.com/" target="_blank">CircleCI</a> subscription so it would be better if we can build app bundles there. Fortunately there is a <code>--local</code> option which allows building app bundles locally. You will still need to set up EAS on Expo, for detail guide, please refer to their <a rel="noreferrer noopener" href="https://docs.expo.dev/build/setup/" target="_blank">setup guide</a>. Here is an example command we use to build our app bundles using CircleCI MacOS and JVM executor.</p>



<pre class="wp-block-code"><code lang="bash" class="language-bash">eas build --profile preview --platform ios --clear-cache --local --non-interactive --output "my-app.ipa"</code></pre>



<p style="font-size:18px">We use <code>eas submit</code> to automatically push master/production build to app store and play store after manual approval. The key here is that for BrowserStack we need to use internal distribution profile so that we can install on their devices. You don&#8217;t have to add their remote devices to Expo as BrowserStack re-signs the bundles upon upload so that they can be installed on their devices.</p>



<h2 class="wp-block-heading">BrowserStack</h2>



<p style="font-size:18px">To make current manual testing easier I decided to introduce <a rel="noreferrer noopener" href="https://www.browserstack.com/docs/app-live" target="_blank">BrowserStack App Live</a>, it allows the team to test the app using remote real devices and it offers quite a lot of iOS and Android options. It also comes with <a rel="noreferrer noopener" href="https://www.browserstack.com/docs/local-testing" target="_blank">local testing</a> which allows us to connect to our backend services hosted behind our vpn. There are some options for getting access to the app. You can upload app <code>.ipa</code> and <code>.apk</code> bundles, or install via <a href="https://apps.apple.com/us/app/testflight/id899247664">Apple TestFlight</a> and <a rel="noreferrer noopener" href="https://play.google.com/store/" target="_blank">Google Play</a>. To test push notification, please refer to guide <a rel="noreferrer noopener" href="https://www.browserstack.com/question/40307" target="_blank">here</a>. If you are uploading the <code>.ipa</code> Apple bundle, unfortunately it is not supported at the time of writing, the only way to test push notification on iOS is installation via TestFlight.</p>



<h2 class="wp-block-heading">Overall Pipeline View</h2>



<p style="font-size:18px">Here is what our overall pipeline looks like:</p>



<figure class="wp-block-image size-large"><img fetchpriority="high" decoding="async" width="1024" height="441" src="https://zhijunchen.com/wp-content/uploads/2022/08/tutor-app-pipeline-1024x441.png" alt="" class="wp-image-193" srcset="https://zhijunchen.com/wp-content/uploads/2022/08/tutor-app-pipeline-1024x441.png 1024w, https://zhijunchen.com/wp-content/uploads/2022/08/tutor-app-pipeline-300x129.png 300w, https://zhijunchen.com/wp-content/uploads/2022/08/tutor-app-pipeline-768x331.png 768w, https://zhijunchen.com/wp-content/uploads/2022/08/tutor-app-pipeline.png 1372w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p style="font-size:18px">For branch build we have a manual trigger to push bundles to BrowserStack as we don&#8217;t want every commit to go through manual QA process. We only trigger this once the PR has been reviewed and ready for QA. The bundles are also uploaded to S3 so that we can retrieve them and install locally easily. You will need to register your test physical devices with Expo using <code>eas device:create</code> so that the bundles can be installed. See internal distribution setup <a href="https://docs.expo.dev/build/internal-distribution/" target="_blank" rel="noreferrer noopener">here</a>.</p>



<p style="font-size:18px">For master/main branch build we went through the similar process with the only difference that builds are automatically pushed to BrowserStack with a manual trigger to push to production following production profile build. We use <a rel="noreferrer noopener" href="https://github.com/conventional-changelog/standard-version" target="_blank">standard-version</a> to automatically increase our app version so that every build is able to be released. The BrowserStack preview here is quite useful during our review session and following that we will decide whether the candidate version is good enough for release.</p>



<h2 class="wp-block-heading">CircleCI config</h2>



<p style="font-size:18px">Here are some useful CircleCI steps for the pipeline described above:</p>



<h5 class="wp-block-heading">Build Android Using OpenJDK executor</h5>



<pre class="wp-block-code"><code lang="yaml" class="language-yaml">eas-build-android:
    executor: android
    parameters:
      profile: 
        description: distribution profile
        type: string
        default: preview
    environment:
      PROFILE: &lt;&lt; parameters.profile &gt;&gt;
    steps:
      - attach_workspace:
          at: ~/
      - node/install:
          install-yarn: true
          node-version: '16.9.1'
      - run: echo -n $PLAY_STORE_SERVICE_ACCOUNT_KEY | base64 -d  &gt; serviceAccountKey.json        
      - run:
          name: android build
          command: |
            mkdir android_sdk
            curl -O https://dl.google.com/android/repository/commandlinetools-linux-8512546_latest.zip
            unzip commandlinetools-linux-8512546_latest.zip
            mv cmdline-tools android_sdk
            mkdir -p android_sdk/cmdline-tools/tools
            mv -t android_sdk/cmdline-tools/tools android_sdk/cmdline-tools/lib android_sdk/cmdline-tools/bin android_sdk/cmdline-tools/source.properties android_sdk/cmdline-tools/NOTICE.txt
            export ANDROID_SDK_ROOT=$HOME/project/android_sdk
            yes | $ANDROID_SDK_ROOT/cmdline-tools/tools/bin/sdkmanager --licenses || if [[ $? -eq 141 ]]; then true; else exit $?; fi
            npm install -g expo-cli eas-cli
            npm ci
            eas build --profile &lt;&lt; parameters.profile &gt;&gt; --platform android --clear-cache --local --non-interactive --output "${CIRCLE_BRANCH//\//-}-${PROFILE}-${CIRCLE_SHA1:0:7}.apk"</code></pre>



<h5 class="wp-block-heading">Build iOS using MacOS executor</h5>



<pre class="wp-block-code"><code lang="yaml" class="language-yaml">  eas-build-ios:
    executor: ios
    parameters:
      profile: 
        description: eas profile
        type: string
        default: preview
    environment:
      PROFILE: &lt;&lt; parameters.profile &gt;&gt;
    steps:
      - attach_workspace:
          at: ~/
      - node/install:
          install-yarn: true
          node-version: '16.9.1'
      - run:
          name: ios build
          command: |
            npm install -g expo-cli eas-cli
            npm ci
            eas build --profile &lt;&lt; parameters.profile &gt;&gt; --platform ios --clear-cache --local --non-interactive --output "${CIRCLE_BRANCH//\//-}-${PROFILE}-${CIRCLE_SHA1:0:7}.ipa"</code></pre>



<p style="font-size:18px">We are using <a href="https://circleci.com/developer/orbs/orb/circleci/node">CircleCI NodeJS Orbs</a> to install NodeJS on the executors. For Android we pass in service account key from pipeline secret and need to download <a rel="noreferrer noopener" href="https://developer.android.com/studio/command-line" target="_blank">Command Line Tools</a> too.</p>



<p style="font-size:18px">Overall the pipeline works quite well for us and we have significant improvements on development process. Now product manager/designer and manual QA could easily download the bundle and play with the app on either BrowserStack or our test physical devices.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How We Are Migrating Monolith To Microservices On AWS</title>
		<link>https://zhijunchen.com/how-we-are-migrating-monolith-to-microservices-on-aws/</link>
		
		<dc:creator><![CDATA[Zhijun Chen]]></dc:creator>
		<pubDate>Fri, 29 Apr 2022 22:13:57 +0000</pubDate>
				<category><![CDATA[Architecture]]></category>
		<category><![CDATA[Kafka]]></category>
		<category><![CDATA[Microservices]]></category>
		<guid isPermaLink="false">https://zhijunchen.com/?p=174</guid>

					<description><![CDATA[Our legacy application is a monolith hosting on multiple EC2 machines on AWS. We are currently under the process of migrating it to microservices architecture mainly due to the following reasons: It is not modular and things are tightly coupled so teams are stepping on each other&#8217;s toes while developing on it The technology is [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Our legacy application is a monolith hosting on multiple EC2 machines on AWS. We are currently under the process of migrating it to microservices architecture mainly due to the following reasons:</p>



<ul><li>It is not modular and things are tightly coupled so teams are stepping on each other&#8217;s toes while developing on it</li><li>The technology is outdated and we don&#8217;t have the expertise on continuous maintenance and development</li><li>The application is poorly tested in general therefore we don&#8217;t feel safe to change the system. One simple change could result in bugs in other places we are not aware of.</li><li>Various performance issues</li></ul>



<p>The migration comes with the following challenges:</p>



<ul><li>We still need to keep the monolith running and serving our customers.</li><li>We can&#8217;t afford a complete rewrite of the monolith as that would take ages.</li></ul>



<p>Our team decided to follow <a rel="noreferrer noopener" href="https://martinfowler.com/bliki/StranglerFigApplication.html" target="_blank">Strangler Fig Pattern</a> and gradually tear the monolith apart.</p>



<p>Here is how the entire migration process looks like:</p>



<figure class="wp-block-image size-full"><img decoding="async" width="686" height="630" src="//i3.wp.com/zhijunchen.com/wp-content/uploads/2022/04/migration-3.png" alt="" class="wp-image-181" srcset="https://zhijunchen.com/wp-content/uploads/2022/04/migration-3.png 686w, https://zhijunchen.com/wp-content/uploads/2022/04/migration-3-300x276.png 300w" sizes="(max-width: 686px) 100vw, 686px" /><figcaption>Monolith Migration</figcaption></figure>



<p>In order for this to work we need to identify our bounded contexts/domains following <a rel="noreferrer noopener" href="https://martinfowler.com/bliki/DomainDrivenDesign.html" target="_blank">domain-driven design</a>. One useful way for this process would be <a rel="noreferrer noopener" href="https://techbeacon.com/app-dev-testing/introduction-event-storming-easy-way-achieve-domain-driven-design" target="_blank">Event Storming</a>. The In this case Domain A would be represented in the diagram as Frontend Application A, Backend Microservice A and Database A, these will run inside ECS or EKS behind a Service Mesh.</p>



<p>We take what we need from the monolith by streaming change log from RDS read replica via <a rel="noreferrer noopener" href="https://docs.aws.amazon.com/dms/latest/userguide/Welcome.html" target="_blank">AWS Data Migration Service</a> to <a rel="noreferrer noopener" href="https://aws.amazon.com/msk/" target="_blank">AWS MSK</a> topic, which will give us <a href="https://kafka.apache.org/" target="_blank" rel="noreferrer noopener">Kafka</a> events which look like the following:</p>



<pre class="wp-block-code"><code>{
  "topic": "&lt;topic_name>",
  "partition": 3,
  "offset": 53870799,
  "tstype": "create",
  "ts": 1651268333902,
  "broker": 2,
  "key": "&lt;partition_key>",
  "payload": "&lt;the_actual_payload>",
  "_datetime": "2022-04-29T21:38:53Z",
  "_payload": {
    "data": {
      "id": 1,
      "createdOn": "2021-01-23T20:38:16Z",
      "modifiedOn": "2022-04-29T15:45:04Z",
      "version": 0,
      "firstName": "Foo 2",
      "lastName": "Bar 2"
    },
    "before": {
      "id": "1",
      "firstName": "Foo",
      "lastName": "Bar"
    },
    "metadata": {
      "timestamp": "2022-04-29T21:38:53.752816Z",
      "record-type": "data",
      "operation": "update",
      "partition-key-type": "primary-key",
      "schema-name": "&lt;schema_name>",
      "table-name": "&lt;table_name>",
      "transaction-id": 569130413019234
    }
  }
}</code></pre>



<p>Since we are already on AWS we try to its services available. If you are not on AWS, then AWS Data Migration Service could be replaced with <a href="https://debezium.io/" target="_blank" rel="noreferrer noopener">Debezium</a>.</p>



<p>Then we create a CDC service transforming raw change event into domain event, this is acting as our ACL (Anti Corruption Layer).</p>



<p>From now on inside each microservice we have Kafka consumers to pick up the events and persist it into individual databases.</p>



<p>On Application Load Balance layer we will forward requests to either old monolith or new microservice frontend based on routing rules. If we are happy with the new microservice then we will deprecate things inside the monolith and make it smaller. Repeating the process would eventually allow us to split the monolith.</p>



<p>During the process monolith application would likely need the data from the new microservices, we can either follow the same approach to feed the data back. How kafka consumer interacts with monolith would depend on individual cases.</p>



<p>There will a lot of issues to resolve along the way but hopefully this will get us where we want to be in future.</p>



<p>Here are some important things to bear in mind during the migration process:</p>



<ul><li>Think about whether what you need is just modular monolith instead of microservices architecture.</li><li>Keep minimal changes to the monolith.</li><li>Start with the most independent domain.</li><li>Follow Kafka best practices, e.g. make sure Kafka consumers are idempotent.</li><li>Have faith that all these work will eventually be paid off as it will be a long journey.</li></ul>



<p></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Mobile Push Notification Overview Using AWS SNS And React Native</title>
		<link>https://zhijunchen.com/mobile-push-notification-overview-using-aws-sns-and-react-native/</link>
		
		<dc:creator><![CDATA[Zhijun Chen]]></dc:creator>
		<pubDate>Sat, 29 Jan 2022 14:29:49 +0000</pubDate>
				<category><![CDATA[Architecture]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[React Native]]></category>
		<category><![CDATA[SNS]]></category>
		<guid isPermaLink="false">https://zhijunchen.com/?p=165</guid>

					<description><![CDATA[Recently I spent some time looking into mobile push notification architecture mainly focused on Android and iOS. Before I go into explaining how mobile push notification works, here are some terminologies I used throughout the post: Platform &#8211; These are services provided by mobile device builders (e.g. Apple, Google, Amazon) that transport the messages to [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Recently I spent some time looking into mobile push notification architecture mainly focused on Android and iOS. Before I go into explaining how mobile push notification works, here are some terminologies I used throughout the post:</p>



<p><strong>Platform</strong> &#8211; These are services provided by mobile device builders (e.g. Apple, Google, Amazon) that transport the messages to the end devices. The Platforms offer device registration, app token creation and management, and the delivery channel for notifications to be delivered to devices. There are several different services that you can use to send push notifications to the users of your applications. The platform you use largely depends on which app store your customers use to obtain your app. The most common platforms are <a rel="noreferrer noopener" href="https://developer.apple.com/notifications/" data-type="URL" data-id="https://developer.apple.com/notifications/" target="_blank">Apple Push Notification service (APNs)</a>, <a rel="noreferrer noopener" href="https://firebase.google.com/docs/cloud-messaging" data-type="URL" data-id="https://firebase.google.com/docs/cloud-messaging" target="_blank">Firebase Cloud Messaging (FCM)</a>, <a rel="noreferrer noopener" href="https://push.baidu.com/" data-type="URL" data-id="https://push.baidu.com/" target="_blank">Baidu Cloud Push</a>, and <a href="https://developer.amazon.com/docs/adm/overview.html" data-type="URL" data-id="https://developer.amazon.com/docs/adm/overview.html" target="_blank" rel="noreferrer noopener">Amazon Device Messaging (ADM)</a>.</p>



<p><strong>Provider</strong> &#8211; The Provider allows app registration and messaging to be coordinated by your mobile apps backend system, to allow for event or schedule based interactions from your backend with your mobile app. They typically utilise underlining mobile platforms to communicate with your mobile app. Examples are <a href="https://docs.aws.amazon.com/sns/latest/dg/welcome.html" data-type="URL" data-id="https://docs.aws.amazon.com/sns/latest/dg/welcome.html" target="_blank" rel="noreferrer noopener">AWS SNS</a>, <a href="https://aws.amazon.com/pinpoint/" data-type="URL" data-id="https://aws.amazon.com/pinpoint/" target="_blank" rel="noreferrer noopener">AWS Pinpoint</a>, <a href="https://onesignal.com/" data-type="URL" data-id="https://onesignal.com/" target="_blank" rel="noreferrer noopener">OneSignal</a>, <a href="https://docs.expo.dev/push-notifications/overview/" data-type="URL" data-id="https://docs.expo.dev/push-notifications/overview/" target="_blank" rel="noreferrer noopener">Expo Push API</a>, etc.</p>



<p><strong>Client</strong> &#8211; This is the mobile application running on a physical device or simulator.</p>



<p>The overall architecture looks like the following:</p>



<figure class="wp-block-image size-full"><img decoding="async" width="812" height="422" src="//i3.wp.com/zhijunchen.com/wp-content/uploads/2022/01/mobile-push-Overall-Architecure.drawio.png" alt="" class="wp-image-166" srcset="https://zhijunchen.com/wp-content/uploads/2022/01/mobile-push-Overall-Architecure.drawio.png 812w, https://zhijunchen.com/wp-content/uploads/2022/01/mobile-push-Overall-Architecure.drawio-300x156.png 300w, https://zhijunchen.com/wp-content/uploads/2022/01/mobile-push-Overall-Architecure.drawio-768x399.png 768w" sizes="(max-width: 812px) 100vw, 812px" /><figcaption>Mobile Push Notification Architecture Overview</figcaption></figure>



<p>As you can see from the diagram, your service/push trigger will talk to AWS SNS which uses FCM/APNs to push notifications to clients. Note that FCM could also push notifications to iOS devices according to their documentation, but underneath it is also using APNs which you still need to setup.</p>



<p>The first thing you need to do is to create a project in your chosen mobile platform, then use the project information to create a <strong>platform application</strong> in SNS. The push notification workflow using SNS is shown as below:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="701" height="651" src="//i1.wp.com/zhijunchen.com/wp-content/uploads/2022/01/mobile-push-Workflow-Workflow.png" alt="" class="wp-image-169" srcset="https://zhijunchen.com/wp-content/uploads/2022/01/mobile-push-Workflow-Workflow.png 701w, https://zhijunchen.com/wp-content/uploads/2022/01/mobile-push-Workflow-Workflow-300x279.png 300w" sizes="(max-width: 701px) 100vw, 701px" /><figcaption>SNS mobile push notification workflow</figcaption></figure>



<ol><li>Your mobile app sets up secure connection with mobile platforms to register for app push notification and request a device and app specific <strong>device token</strong></li><li>The platform returns the device token back to your mobile app</li><li>Your mobile app could pass the device token along with other information to your service</li><li>You service calls AWS SNS to set up an <strong>application endpoint</strong> using the device token and additional user data</li><li>SNS returns application endpoint response back to your service to save for future reference</li><li>When your service wants to push a notification, it will call SNS application endpoint with the message for the platform</li><li>SNS calls mobile platform to deliver the message</li><li>Mobile platform pushes the message to your mobile client</li></ol>



<p>There are several react native mobile libraries you can use for step 1-2:</p>



<ul><li>If you are using <a rel="noreferrer noopener" href="https://expo.dev/" data-type="URL" data-id="https://expo.dev/" target="_blank">Expo</a> to create your mobile application, you can use their <a rel="noreferrer noopener" href="https://www.npmjs.com/package/expo-notifications" data-type="URL" data-id="https://www.npmjs.com/package/expo-notifications" target="_blank">expo-notifications</a> to receive notifications and call <a rel="noreferrer noopener" href="https://docs.expo.dev/versions/latest/sdk/notifications/#getdevicepushtokenasync-devicepushtoken" target="_blank">Notifications.getDevicePushTokenAsync()</a> to retrieve native token instead of Expo token. At the time of writing, this approach does not work with Expo Go app and the apple environment is default to production. This library works best with <a rel="noreferrer noopener" href="https://docs.expo.dev/push-notifications/overview/" data-type="URL" data-id="https://docs.expo.dev/push-notifications/overview/" target="_blank">Expo Push API</a> so you might need to put in extra effort to set it up and it also requires physical device in both Apple and Android for push notification to work. One advantage of using this library is that you write in pure JavaScript without having to set up Android and iOS native code.</li><li>If you are using reactive native, there are <a rel="noreferrer noopener" href="https://github.com/zo0r/react-native-push-notification" data-type="URL" data-id="https://github.com/zo0r/react-native-push-notification" target="_blank">react-native-push-notification</a> (<strong>Warning: No longer actively maintained</strong>) and <a rel="noreferrer noopener" href="https://github.com/wix/react-native-notifications" target="_blank">react-native-notifications</a>.</li></ul>



<p><strong>Note: Apple always requires physical devices for push notification while Android simulator could be used to test locally.</strong></p>



<p>One important thing I found out through the research was that there are so many solutions out there and each of them gives you pros and cons. It is better to gather your requirements first then pick the one most suitable for you.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
