Skip to content

Refactoring: removed duplications in swap rf-db access paths #22314

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 26, 2025

Conversation

vkjr
Copy link
Contributor

@vkjr vkjr commented Mar 17, 2025

Summary

Problem

We duplicate same rf-db path sections multiple times in our codebase. For example :wallet :ui :swap in code sections similar to this one:

              (assoc-in [:wallet :ui :swap :asset-to-pay] asset-to-pay)
              (assoc-in [:wallet :ui :swap :asset-to-receive] asset-to-receive)
              (assoc-in [:wallet :ui :swap :network] network')
              (assoc-in [:wallet :ui :swap :launch-screen] view-id)
              (assoc-in [:wallet :ui :swap :start-point] start-point)

That means that any database refactoring will be a pain. In fact it is the same problem as sprinkling some magic constant around the code - tons of places will need to change if constants changed.

Lets check how things are with the send constants:
Screenshot 2025-03-13 at 14 37 38
124 occurences!

Decision

This PR reimplements suggestions proposed by @flexsurfer in #22288 - combining update-in with assoc/dissoc and proper destructuring to get rid of key sequence duplication. Plus It introduces constant db/swap for [:wallet :ui :swap] db path.

(update-in db db/swap
           assoc
           :asset-to-pay asset-to-pay
           :asset-to-receive asset-to-receive
           :network network'
           :launch-screen view-id
           :start-point start-point)
(let [{:keys [last-request-uuid 
              amount-hex 
              asset-to-pay 
              asset-to-receive 
              network 
              initial-response?]} 
      (get-in db db/swap)]
  ...)

Review notes

This PR only introduces constant for swap db path. If you will find it useful the approach can be spread to more code areas

Testing notes

Swaps internals changed, they need testing

Platforms

  • Android
  • iOS

Areas that may be impacted

Functional

  • wallet / transactions

Steps to test

  • Open Status
  • Do some swaps
  • Step 3, etc.

status: ready

@status-im-auto
Copy link
Member

status-im-auto commented Mar 17, 2025

Jenkins Builds

Click to see older builds (13)
Commit #️⃣ Finished (UTC) Duration Platform Result
138b652 #1 2025-03-17 12:48:22 ~3 min tests 📄log
✔️ 47fc8aa #2 2025-03-17 12:53:27 ~4 min tests 📄log
✔️ 47fc8aa #2 2025-03-17 12:56:21 ~7 min android-e2e 🤖apk 📲
✔️ 47fc8aa #2 2025-03-17 12:57:42 ~9 min android 🤖apk 📲
✔️ 47fc8aa #2 2025-03-17 13:04:09 ~15 min ios 📱ipa 📲
✔️ b6bac7d #4 2025-03-23 19:35:44 ~5 min tests 📄log
✔️ b6bac7d #4 2025-03-23 19:38:24 ~7 min android-e2e 🤖apk 📲
✔️ b6bac7d #4 2025-03-23 19:39:13 ~8 min android 🤖apk 📲
✔️ b6bac7d #4 2025-03-23 19:40:59 ~10 min ios 📱ipa 📲
✔️ ca55e5f #5 2025-03-24 17:40:15 ~6 min tests 📄log
✔️ ca55e5f #5 2025-03-24 17:44:14 ~10 min android 🤖apk 📲
✔️ ca55e5f #5 2025-03-24 17:45:04 ~11 min android-e2e 🤖apk 📲
✔️ ca55e5f #5 2025-03-24 17:45:37 ~11 min ios 📱ipa 📲
Commit #️⃣ Finished (UTC) Duration Platform Result
✔️ a526e4b #6 2025-03-25 14:57:09 ~7 min tests 📄log
✔️ a526e4b #6 2025-03-25 14:58:23 ~8 min android-e2e 🤖apk 📲
✔️ a526e4b #6 2025-03-25 14:58:31 ~8 min android 🤖apk 📲
✔️ a526e4b #6 2025-03-25 15:01:18 ~11 min ios 📱ipa 📲
✔️ 7ea3617 #7 2025-03-26 09:40:16 ~5 min tests 📄log
✔️ 7ea3617 #7 2025-03-26 09:44:05 ~9 min android-e2e 🤖apk 📲
✔️ 7ea3617 #7 2025-03-26 09:44:44 ~9 min android 🤖apk 📲
✔️ 7ea3617 #7 2025-03-26 09:49:32 ~14 min ios 📱ipa 📲

@shivekkhurana shivekkhurana added the wallet-core Issues for mobile wallet team label Mar 19, 2025
@vkjr
Copy link
Contributor Author

vkjr commented Mar 19, 2025

@flexsurfer, @alwx, @seanstrom, @ulisesmac, would you mind to take a look?

Copy link
Member

@seanstrom seanstrom left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well done! ✅

Left some small comments, let me know what you think 🙏

[status-im.contexts.wallet.sheets.buy-network-selection.view :as buy-network-selection]
[status-im.contexts.wallet.sheets.select-asset.view :as select-asset]
[utils.i18n :as i18n]))

(rf/reg-event-fx :wallet.buy-crypto/select-provider
(fn [{:keys [db]} [{:keys [account provider recurrent?]}]]
(let [swap-network (get-in db [:wallet :ui :swap :network])]
(let [{swap-network :network} (get-in db db/swap)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it ✨

@@ -786,7 +786,7 @@
(fn [{:keys [db]}
[{sent-transactions :sentTransactions
send-details :sendDetails}]]
(let [swap? (get-in db [:wallet :ui :swap])]
(let [swap? (get-in db db/swap)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small question, do we always dissoc this state when it's not used? or is possible that it could be an empty map?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

better to have swap? as bool, so it should use seq

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@seanstrom, well we should dissoc it always but I can imagine that sometimes it is not the case due to bug.

@flexsurfer, sorry I didn't quite get what do you mean, could you please elaborate? 🙏

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i mean (get-in db [:wallet :ui :swap]) is a map right? but swap? should be bool

Comment on lines 4 to 5
(def swap [:wallet :ui :swap])

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small nit:

swap could be a confusing name since it can be read almost like a verb, like "we're about to swap the db" or something. We could consider re-naming it to something like swap-ui or wallet-swap. What do you think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about swaps?

Copy link
Member

@flexsurfer flexsurfer Mar 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not path-swap?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not path-swap?

Yeah that's better, but maybe reverse to swap-path, so when we have more paths, it's easier to find with the editor.

Copy link
Contributor Author

@vkjr vkjr Mar 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@clauxx, @seanstrom, @flexsurfer, we can have a namespace db-path in a file db-path.cljs with that constant defined (and others later). That will result in more idiomatic db-path/swap. wdyt?

Copy link
Member

@clauxx clauxx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm. I like this approach ⚡

Copy link
Member

@flexsurfer flexsurfer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a few small changes required

Copy link
Contributor

@ulisesmac ulisesmac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this changes! @vkjr

I like the code organization and looks a lot better.

Are we going to introduce this into the guidelines? I think if all the paths would follow this approach they'd be easier to maintain.

:pending
(= tx-status constants/transaction-status-failed)
:failed)
{:keys [swap-approval-transaction-id]} (get-in db db/swap)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wondering if (conj db/swap :swap-approval-transaction-id) is better since we avoid a long let binding alignment. wdyt?

Copy link
Contributor Author

@vkjr vkjr Mar 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Within let it would be longer actually:

{:keys [swap-approval-transaction-id]} (get-in db db-path/swap)
swap-approval-transaction-id           (get-in db (conj db/swap :swap-approval-transaction-id))

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But it won't force all bindings to be displaced too far.

It's fine, just I don't like (as others, I guess) when all the code is significantly moved to the right

@vkjr
Copy link
Contributor Author

vkjr commented Mar 23, 2025

Are we going to introduce this into the guidelines? I think if all the paths would follow this approach they'd be easier to maintain.

@ulisesmac, I think we can update more places in code to make it "guideline by example" first and than put to words in a document if needed, wdyt? :)

@vkjr vkjr requested a review from flexsurfer March 23, 2025 19:28
@vkjr vkjr force-pushed the chore/db-paths-2 branch from 17bceef to b6bac7d Compare March 23, 2025 19:30
@vkjr
Copy link
Contributor Author

vkjr commented Mar 24, 2025

@flexsurfer, changes made, could you please take a look?

@@ -795,7 +796,9 @@
(fn [{:keys [db]}
[{sent-transactions :sentTransactions
send-details :sendDetails}]]
(let [swap? (get-in db [:wallet :ui :swap])]
(let [swap? (-> db
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-> db not needed here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@flexsurfer, thanks for catching! fixed

@vkjr vkjr moved this from REVIEW to E2E Tests in Pipeline for QA Mar 25, 2025
@VolodLytvynenko VolodLytvynenko moved this from E2E Tests to IN TESTING in Pipeline for QA Mar 25, 2025
@VolodLytvynenko VolodLytvynenko self-assigned this Mar 25, 2025
@ulisesmac
Copy link
Contributor

Are we going to introduce this into the guidelines? I think if all the paths would follow this approach they'd be easier to maintain.

@ulisesmac, I think we can update more places in code to make it "guideline by example" first and than put to words in a document if needed, wdyt? :)

Well, if this is an approach already agreed by the team, I think it should be in the guidelines so that all code added follows it.

Also, maybe we should clarify that this is the approach recommended when possible, idk if there could be some event handlers where this is hard to achieve.

wdyt?

cc: @ilmotta

@status-im-auto
Copy link
Member

36% of end-end tests have passed

Total executed tests: 14
Failed tests: 7
Expected to fail tests: 2
Passed tests: 5
IDs of failed tests: 727231,741555,727230,740490,741612,741554,727229 
IDs of expected to fail tests: 741840,741841 

Failed tests (7)

Click to expand
  • Rerun failed tests

  • Class TestWalletMultipleDevice:

    1. test_wallet_send_asset_from_drawer, id: 727230

    Device 2: Find `Text` by `xpath`: `//android.view.ViewGroup[@content-desc='container']/android.widget.TextView[@text='Ether']/../android.widget.TextView[3]`
    Device 2: `Text` is `0.21699 ETH`

    critical/wallet/test_wallet_testnet.py:202: in test_wallet_send_asset_from_drawer
        self.errors.verify_no_errors()
    base_test_case.py:179: in verify_no_errors
        pytest.fail('\n '.join([self.errors.pop(0) for _ in range(len(self.errors))]))
     Device 1: Eth amount in the shdUaM8M6QcxQ4qn32nQ's wallet is 0.4634 but should be 0.2464
    



    2. test_wallet_send_eth, id: 727229

    Device 2: Find Text by xpath: //android.view.ViewGroup[@content-desc='container']/android.widget.TextView[@text='Ether']/../android.widget.TextView[3]
    Device 2: Text is 0.21689 ETH

    critical/wallet/test_wallet_testnet.py:167: in test_wallet_send_eth
        self.errors.verify_no_errors()
    base_test_case.py:179: in verify_no_errors
        pytest.fail('\n '.join([self.errors.pop(0) for _ in range(len(self.errors))]))
     Device 1: Eth amount in the shdUaM8M6QcxQ4qn32nQ's wallet is 0.4634 but should be 0.2465
    



    Class TestWalletOneDevice:

    1. test_wallet_add_remove_regular_account, id: 727231

    Test setup failed: critical/wallet/test_wallet_mainnet.py:36: in prepare_devices
        self.sign_in_view.sign_in(user_name=self.sender_username)
    ../views/sign_in_view.py:134: in sign_in
        self.get_user_profile_by_name(user_name).click()
    ../views/sign_in_view.py:147: in get_user_profile_by_name
        raise NoSuchElementException(msg="User %s is not found!" % username)
     User shdUaM8M6QcxQ4qn32nQ is not found!; For documentation on this error, please visit: https://www.selenium.dev/documentation/webdriver/troubleshooting/errors#no-such-element-exception
    



    2. test_wallet_swap_flow_mainnet, id: 741555

    Test setup failed: critical/wallet/test_wallet_mainnet.py:36: in prepare_devices
        self.sign_in_view.sign_in(user_name=self.sender_username)
    ../views/sign_in_view.py:134: in sign_in
        self.get_user_profile_by_name(user_name).click()
    ../views/sign_in_view.py:147: in get_user_profile_by_name
        raise NoSuchElementException(msg="User %s is not found!" % username)
     User shdUaM8M6QcxQ4qn32nQ is not found!; For documentation on this error, please visit: https://www.selenium.dev/documentation/webdriver/troubleshooting/errors#no-such-element-exception
    



    3. test_wallet_balance_mainnet, id: 740490

    ## Sign in (password: qwerty1234)
    Device 1: Getting username card by 'shdUaM8M6QcxQ4qn32nQ'

    Test setup failed: critical/wallet/test_wallet_mainnet.py:36: in prepare_devices
        self.sign_in_view.sign_in(user_name=self.sender_username)
    ../views/sign_in_view.py:134: in sign_in
        self.get_user_profile_by_name(user_name).click()
    ../views/sign_in_view.py:147: in get_user_profile_by_name
        raise NoSuchElementException(msg="User %s is not found!" % username)
     User shdUaM8M6QcxQ4qn32nQ is not found!; For documentation on this error, please visit: https://www.selenium.dev/documentation/webdriver/troubleshooting/errors#no-such-element-exception
    



    4. test_wallet_bridge_flow_mainnet, id: 741612

    Test setup failed: critical/wallet/test_wallet_mainnet.py:36: in prepare_devices
        self.sign_in_view.sign_in(user_name=self.sender_username)
    ../views/sign_in_view.py:134: in sign_in
        self.get_user_profile_by_name(user_name).click()
    ../views/sign_in_view.py:147: in get_user_profile_by_name
        raise NoSuchElementException(msg="User %s is not found!" % username)
     User shdUaM8M6QcxQ4qn32nQ is not found!; For documentation on this error, please visit: https://www.selenium.dev/documentation/webdriver/troubleshooting/errors#no-such-element-exception
    



    5. test_wallet_send_flow_mainnet, id: 741554

    Test setup failed: critical/wallet/test_wallet_mainnet.py:36: in prepare_devices
        self.sign_in_view.sign_in(user_name=self.sender_username)
    ../views/sign_in_view.py:134: in sign_in
        self.get_user_profile_by_name(user_name).click()
    ../views/sign_in_view.py:147: in get_user_profile_by_name
        raise NoSuchElementException(msg="User %s is not found!" % username)
     User shdUaM8M6QcxQ4qn32nQ is not found!; For documentation on this error, please visit: https://www.selenium.dev/documentation/webdriver/troubleshooting/errors#no-such-element-exception
    



    Expected to fail tests (2)

    Click to expand

    Class TestWalletCollectibles:

    1. test_wallet_send_collectible, id: 741840

    Device 1: Tap on found: Button
    Device 1: Find EditBox by accessibility id: address-text-input

    critical/wallet/test_collectibles.py:102: in test_wallet_send_collectible
        self.wallet_view.address_text_input.send_keys(self.receiver['wallet_address'])
    ../views/base_element.py:365: in send_keys
        self.find_element().send_keys(value)
    ../views/base_element.py:78: in find_element
        raise NoSuchElementException(
     Device 1: EditBox by accessibility id: `address-text-input` is not found on the screen; For documentation on this error, please visit: https://www.selenium.dev/documentation/webdriver/troubleshooting/errors#no-such-element-exception 
    

    [[Collectibles list is not loaded sometimes]]

    2. test_wallet_collectible_send_from_expanded_info_view, id: 741841

    Device 1: Find Button by accessibility id: collectibles-tab
    Device 1: Tap on found: Button

    critical/wallet/test_collectibles.py:158: in test_wallet_collectible_send_from_expanded_info_view
        self.wallet_view.get_collectible_element('Glitch Punks').wait_for_element().click()
    ../views/base_element.py:120: in wait_for_element
        raise TimeoutException(
     Device `1`: `CollectibleItemElement` by` xpath`: `//*[@content-desc='collectible-list-item']//*[contains(@text,'Glitch Punks')]/../..` is not found on the screen after wait_for_element 
    

    [[Collectibles list is not loaded sometimes]]

    Passed tests (5)

    Click to expand

    Class TestWalletCollectibles:

    1. test_wallet_collectibles_balance, id: 741839

    Class TestCommunityOneDeviceMerged:

    1. test_restore_multiaccount_with_waku_backup_remove_profile_switch, id: 703133
    Device sessions

    2. test_community_copy_and_paste_message_in_chat_input, id: 702742
    Device sessions

    Class TestCommunityMultipleDeviceMerged:

    1. test_community_message_edit, id: 702843
    Device sessions

    Class TestOneToOneChatMultipleSharedDevicesNewUi:

    1. test_1_1_chat_non_latin_messages_stack_update_profile_photo, id: 702745
    Device sessions

    @VolodLytvynenko
    Copy link
    Contributor

    Hi @vkjr no issues from my side. PR can be merged. Here are the checked flows:

    How the Flow is Started
    ✅ Go to the main wallet page → Long-tap on an asset → Select "Swap" option
    ✅ Open account → Long-tap on an asset → Select "Swap" option
    ✅ Open account → Tap "Swap" button
    ✅ Go to the main wallet page → Tap "Swap" button

    Used Networks:
    ✅ Optimism (Opt)
    ✅ Base
    ✅ Arbitrum (Arb)
    ✅ Ethereum (Eth)

    Conditions on Swap Setup
    ETH Transactions
    ✅ ETH → Any value → Min available boundary
    ✅ ETH → Min boundary 0.01 → Any available value
    ✅ ETH → Max boundary 30 → Max available value (use "Max" button)
    ✅ ETH → Any value

    ERC-20 Transactions
    ✅ ERC-20 → Any value → Min available boundary
    ✅ ERC-20 → Max boundary 30 → Any available value
    ✅ ERC-20 → Min boundary 0.01 → Max available value for swap (use "Max" button)
    ✅ ERC-20 → Any value

    Additional tests:
    ✅ ETH is selected in receiver if USDC is selected during swap flow
    ✅ "Approve" button is shown if entered value is out of approved value
    ✅ ERC-20 Approval + Approval + Swap + Revoke
    ✅ Assets to pay / assets to receive correct assets are fetched
    ✅ Swap different error handling
    ✅ Entered value is rounded on: swap page, slippage drawer, approval and swap confirmation

    @VolodLytvynenko VolodLytvynenko moved this from IN TESTING to MERGE in Pipeline for QA Mar 25, 2025
    @vkjr vkjr force-pushed the chore/db-paths-2 branch from a526e4b to 7ea3617 Compare March 26, 2025 09:34
    @vkjr vkjr merged commit 91df9f9 into develop Mar 26, 2025
    5 checks passed
    @vkjr vkjr deleted the chore/db-paths-2 branch March 26, 2025 09:52
    @github-project-automation github-project-automation bot moved this from MERGE to DONE in Pipeline for QA Mar 26, 2025
    @ilmotta
    Copy link
    Contributor

    ilmotta commented Apr 1, 2025

    Also, maybe we should clarify that this is the approach recommended when possible, idk if there could be some event handlers where this is hard to achieve.

    @vkjr @ulisesmac, @seanstrom, @alwx, @flexsurfer, @clauxx, just one observation from this PR. The PR solution is completely fine for reducing duplication, but it's also solving a problem that exists from the choice of nesting maps in the app db. If we used less nesting and more root keys with good namespacing and qualification, we would:

    1. Cheaply and safely refactor however many number of keys because they would be mostly guaranteed to be unique across the repo and the language server would do the job for us.
    2. There would be no need to for special constants to name access paths to the db as in the PR.

    Essentially the guideline suggestion from @ulisesmac is a good one in the sense that this nesting pattern is growing in usage in the repo per the majority preference, but wouldn't be necessary if we kept the app db flat (or more flat, as it used to be in the past).

    @vkjr
    Copy link
    Contributor Author

    vkjr commented Apr 1, 2025

    @ilmotta,

    it's also solving a problem that exists from the choice of nesting maps in the app db

    Not sure I can agree with such direct conclusion. I tend to think that problem solved by this PR is not a result of nesting itself but a result of its incorrect usage.

    Big refactorings could occur for any approach including using fully-flat structure, only the reasons would be different.
    Example: we could eventually come up with understanding that approach for naming keys doesn't cover our needs anymore. Changing it would require renaming all keys - big refactoring. More keys with longer namespaces means more verbose code, more verbose subscriptions.
    Also we are not sure how flat structure affects the performance because root keys are always compared on db change.

    So there are pros and cons of every approach as usually :)
    Overall we discussed db structure few times, also on dx calls and as I perceived the results of the discussion was that we want some "middle ground":

    1. db should have better normalization and less data-duplication, that would result in db being more flat
    2. we still want some level of nesting for convenience (I definitely find it more convenient than having fully-flat structure)
      Please, correct me if I got it wrong.

    And aim of this PR is to facilitate the further improvements of database by reducing constants duplication and simplify future changes. So I consider it a step in the direction that would suit us all :)

    @ilmotta
    Copy link
    Contributor

    ilmotta commented Apr 1, 2025

    Not sure I can agree with such direct conclusion. I tend to think that problem solved by this PR is not a result of nesting itself but a result of its incorrect usage.

    @vkjr It's not my intention to go back to the original discussions about flat versus nesting that we had already, although the topics are related.

    About "incorrect usage", I don't see where the incorrect usage was, just suboptimal Clojure code. The changes I see in the PR are:

    1. Replaced duplicated get calls with destructuring, a common change in Clojure.
    2. Multiple map updates with update-in and assoc, a common refactor we applied many times in the repo.

    The other change in the PR was to extract the path to a constant, which is something we wouldn't do if we didn't nest so much in the app-db. It would make no sense to extract to a constant a root key for instance, so I think my conclusion is reasonable.

    Btw, when I mention "root keys" I don't mean full flattening everywhere. There's always a middle ground.

    we could eventually come up with understanding that approach for naming keys doesn't cover our needs anymore. Changing it would require renaming all keys - big refactoring.

    It's quite different to me. The language server would easily and safely rename the root key in one go in all places. I'm not thinking in terms of big/small refactor. The renaming of a root key is simple and safe because it's globally unique. It would be a quick & easy PR to merge, no need to ask for manual QA either. Renaming nested keys requires a careful analysis of all potential uses of each individual nested key and because Clojure is so dynamic, it's an error prone approach and cumbersome. This is one tradeoff.

    More keys with longer namespaces means more verbose code, more verbose subscriptions.

    But is this really a problem in practice? We could discuss examples to compare how things would look like and how much worse one approach is compared to the other. We also have namespaced keys to our disposal and they support aliasing the qualification part just as a normal import of a var like in db-path/swap. Thus, root keys can be easily shortened if we go this route (which for reasons I don't fully understand are not appreciated in our teams).

    Also we are not sure how flat structure affects the performance because root keys are always compared on db change.

    This doesn't seem to be a problem in general because only mounted subscriptions will be compared. We also know the app performed well for years with the previous approach avoiding nesting.

    Speaking of performance, a tiny detail, I recommend adding the ^:const metadata in another PR to the wallet.db-path/swap so that the compiler will inline the vector and there won't be a lookup overhead.

    we still want some level of nesting for convenience (I definitely find it more convenient than having fully-flat structure)
    Please, correct me if I got it wrong.

    Correct 👍🏼 that's our consensus and I agree with it. Well designed root keys can be quite convenient to consume. They can have the same stem, thus offer autocompletion like in :wallet.ui/*, truly safe renames, reliable to grep by pure text, reliable reference lookups with the language server, compiler-level validation of the qualification part of namespaced keys, and namespaced keys can be aliased if namespaced in order to reduce verbosity.

    I'm curious about the inconveniences of a more flat structure, perhaps I'm envisioning something different than the not so great experience you had. But again, I don't want to start long discussions about flat vs nested.

    This PR only introduces constant for swap db path. If you will find it useful the approach can be spread to more code areas

    We should be mindful about the already significant overhead that is to read code in status-mobile because of the numerous ways of doing the same thing, unfinished refactors, etc. The pattern introduced in this PR raises the important question about when to use it, as @ulisesmac pointed out.

    I would encourage us to avoid introducing patterns without careful evaluation if it would scale well throughout the rest of the repo.

    @vkjr
    Copy link
    Contributor Author

    vkjr commented Apr 2, 2025

    @ilmotta,

    Speaking of performance, a tiny detail, I recommend adding the ^:const metadata in another PR to the wallet.db-path/swap so that the compiler will inline the vector and there won't be a lookup overhead.

    Good point, thanks!

    We have 2 main discussion points right now:

    • flat vs nested structure
    • when and how to use approach raised in this PR (consntants for db-path)

    Flat vs Nested

    Here we probably need a clarification because as you mentioned we can envision different things.
    I envision flat structure as something that we already have in our app.
    Here is the example screenshot of just a part of the db that fitted my screen. There is a chat opened in the app and I marked with red squares all keys that supposedly carry chat data. My biggest struggle as a developer with such structure is that data is too scattered and it is hard for me to grasp the meaning and relationships.

    When I envision nested structure, I think about structure I provided as an example in application layers document.
    And when “incorrect usage of nested structure” was mentioned I meant:

    • too deep nesting
    • too many data (including duplicated) in deep levels of rf-db. Which results in frequent repeating of the same path access sequences like :wallet :ui :send.
      It is not really “incorrect” but more like not “well-forethought”.

    So with these clarifications we can compare if you envision similar things when we talk about flat and nested. Is my vision is different from yours?

    When to use introduced approach

    Shame, I missed the latest question from @ulisesmac in this PR, thanks for bringing it up.
    Not sure how to formulate the guidance for having constants for db path. Current PR I perceive as inevitable “evil” that tames the duplication of too nested and unstructured paths. In the amazing fairytale world of restructured rf-db where root keys are layers I think there shouldn’t be a necessity in sequences longer than 2 keys, like [:app :current-flow] or [:domain :accounts].
    But if sequence reused between events and subs file, or used significant amount of times (like 10 or more), I think it better to be moved to a constant.

    Main thought:

    Taking into account that I also feel that we shouldn't need constants for more than 2-keywords paths in ideal rf-db, I can imagine it would be equally convenient (or even more) to use namespace-prefixed keys like :domain/accounts. If this is the kind of flat structure you envisioned, @ilmotta - that works well for me.
    Still I think it is possible only after db is well-restructured by layers. Because in our current implementation “flat structure” is just a chaotic disorienting bunch of keys :)

    Wdyt?

    cc @ulisesmac

    @ilmotta
    Copy link
    Contributor

    ilmotta commented Apr 7, 2025

    Always enjoyable to discuss technical topics with you @vkjr

    So with these clarifications we can compare if you envision similar things when we talk about flat and nested. Is my vision is different from yours?

    The problem is that the states related to the messenger deserve some love to be consistent and are not a good indicator of what things could look like if the structure was still kept flat(ish). Qualifications could be better used, and we could avoid global names like :reactions.

    I think we mostly agree on the direction. As you said, the app-db layers might be able to unnest and separate concerns well enough that it won't be a problem anymore.

    I envision flat structure as something that we already have in our app.

    Complete flatness is something that I think is undesirable. I envision something like root keys being small tables, properly indexed and normalized, where other parts of the app-db can more or less efficiently join. These so called "tables" could be the immediate children of the layers you've been advocating and that can work quite well.

    The UI state as in for example wallet.ui.foo.bar is a separate thing, because UI state is more ephemeral by nature and can more easily suffer from refactors, so I'd keep things as flexible as possible in this layer. The infra layer from status-go, and the other layer massaging the status-go data, those I'd treat much more carefully and would avoid nesting as a technique for organizing code.

    Still I think it is possible only after db is well-restructured by layers. Because in our current implementation “flat structure” is just a chaotic disorienting bunch of keys :)

    I agree with you 100% on this.

    In the amazing fairytale world of restructured rf-db where root keys are layers I think there shouldn’t be a necessity in sequences longer than 2 keys, like [:app :current-flow] or [:domain :accounts].

    This would be a great upside from the reestructure of the app-db you are trying to bring forward 👍

    @vkjr
    Copy link
    Contributor Author

    vkjr commented Apr 7, 2025

    Always enjoyable to discuss technical topics with you @vkjr

    Thanks, it's likewise 🎩

    I envision something like root keys being small tables, properly indexed and normalized

    Fully agree on this, I expected those "tables" to be especially wide-used for the domain level of the application - it is where we store the status-go data after massaging: chats, messages, tokens, currencies, accounts, etc..

    And In my imagination they always no deeper than 2nd level of nesting:

    {:domain {:accounts {"account-1-id" {.. account data ..}
                         "account-2-id" {.. account data ..}}
              :messages {"message-1-id" {.. message data ..}
                         "message-2-id" {.. message data ..}}
              :tokens   {...}
              :chats    {...}}}

    Which mean they also can be a prefixed root-keys 👍

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    Projects
    Archived in project
    Development

    Successfully merging this pull request may close these issues.

    10 participants