Open bpierre opened 6 years ago
This is amazing. Can we make the repo public and ask in #dev to see what people think about it?
I really like the direction of this.
I think we should keep contract.call('method', ...)
even if we also add contract.get('method', ...)
which would do the same.
Also do you think we should keep the ability to initiate intents by doing contract.method(...)
directly? Renaming contract.intent('method', ...)
to contract.send('method', ...)
would also be more similar to web3.js semantics.
I think we should keep contract.call('method', ...) even if we also add contract.get('method', ...) which would do the same.
Cool let’s keep contract.call()
, but we should probably kill contract.get()
then 😁
Renaming contract.intent('method', ...) to contract.send('method', ...) would also be more similar to web3.js semantics.
OK, let’s use .send()
.
Also do you think we should keep the ability to initiate intents by doing contract.method(...) directly?
Yes I think it’s ok now that it’s on its own object, and that we are not using direct methods for .call()
on the external contract. It will make additions complicated in the future, but I think it’s an acceptable risk.
Hey everyone,
@bpierre I'm so happy to see I'm not the only one who thinks this can be improved to avoid confusion! 😅
Some good ideas in there. I have a couple of thoughts on a few points that I'd just like to leave here. Maybe they help making certain design decisions:
While contract
is a contract instance, externalContract
is a factory function. It may make sense to make that a bit more obvious. So I'd propose something like createExternalContract
or rather getExternalContract
(as we're not really creating here)
On contract.get()
vs contract.call()
- I think here you might want to take into account what the goal is: do you want this API to look similar to web3 APIs, or do you want an API that just makes sense? I personally have to say that calling contract.call('value')
doesn't look like reading a value at all. Obviously, if you're used to web3 APIs, this might be different. Maybe having an alias isn't so bad after all. However .get()
is a little ambiguous as well... getProperty()
?
On intent()
vs send()
- Here I'd also go with an API that makes sense as opposed to one that is similar to web3 APIs. I personally wouldn't mind having intent()
at least as an alias as it's official aragon terminology. Or, and this is going to be funny, call()
would make sense here as well :D
Can you add some more thoughts on how you'd support Observable APIs and callback/promise APIs? While I do get the reasoning that many people struggle with using Observables (we had this a lot in the Angular community), one can always transform an Observable to a promise using toPromise()
operator (although, not recommended). I think if you'd find a way to have first class support for both APIs, that'd be great. I would not replace observable APIs for promise based APIs, just for the sake of being able to use async/await. Observables have tons of powerful operators that developers may want to take advantage of when building apps. Also, as mentioned earlier you can always map to a promise eventually. E.g.
contract.get('value').pipe(
map(...),
filter(...)
).subscribe(value => /* do something with transformed value */)
With await:
await contract.get('value').pipe(
map(...),
filter(...),
toPromise()
)
I think it'd be cool to keep this power.
I have mixed feelings about the createAppState()
name but I also don't have a better alternative at hand right now (plus I'd avoid bikeshedding too much) :)
Hope this makes sense!
We really like those ideas!
For me, RxJs is really interesting when dealing with streams of data that are received over time. But most of AragonApp functions return a value only once, so I think returning a Promise would be simpler for the majority of users. Also, converting a Promise to an Observable is trivial so it wouldn't really hamper people who still want to use RxJs.
It is also great for people like us who plan to support both Aragon and web3.js as it will reasonably simplify our codebase if both libraries return Promises.
With that said, maybe the events()
function would be a good fit for returning an Observable.
We also like the idea of moving contract functions into a separate object, preventing conflicts and making the interface cleaner.
As for the terminology, I personally have a small preference for function names closer to the web3.js API because a lot of users are coming from an Ethereum background where they already have experience with that library. So I think it would be a little bit easier for them if the syntax is similar to what they are used to work with.
~One thing that is not clear for me is the usage of web workers: are they necessary, what problem do they solve? Maybe we can provide an example without them.~ Answer
What I would really love is to use aragon.js
with the redux
tooling that is out there, also to be able to split the reducer in multiple files, etc.
(I have a feeling that is already possible though)
⚠️ Work in progress, posting it early to get some feedback.
Some issues that I see with the current API:
RxJS
Users are required to have some knowledge of RxJS. I think we could provide a simpler API that would replace RxJS by using callbacks (node style) and promises. The RxJS API would still be available to users that want to use it.
Messaging Providers
Aragon.js providers don’t appear like something useful to learn as a new Aragon developer. The concept of messaging providers in Aragon.js could only be exposed to users that want to do something specific with it.
Same API in the worker and the app frontend
Aragon.js is exposing the same constructor on the worker side and on the frontend side. This is elegant and powerful, but the “recommended” way to do things might not be obvious at first. For example, users are expected to use
aragon.store()
in the worker, andaragon.subscribe()
in the frontend.The user-facing API could be designed in a way that makes our intention more obvious, or as Python’s PEP 20 describes it:
Implicitly based on the main contract
Aragon.js is a general API to interact with:
external()
).With the current API, it might not be obvious that
call()
,events()
,store()
andstate()
are referring to the main app contract, while other methods are related to UI features (identify()
,notify()
,context()
), the web3 provider (accounts()
), or the Aragon.js caching mechanism (cache()
).Interacting with the main contract could be done at the same level than the other features (i.e. not on the main object directly).
Direct methods
Methods are defined directly on
AragonApp
instances, which is convenient but could conflict with the other methods available.It also feels inconsistent with external contracts, where direct methods are used to perform read-only calls, while using
.call()
onAragonApp.prototype
.Proposed solution: a “simple” API
This API would be built on top of the current API, and would attempt to solve the issues listed above. It would consist of exposing two different constructors: one for the worker, and one for the frontend app. The Aragon.js providers would not be exposed, and interacting with the main contract would be made more explicit. RxJS observers would be replaced by callbacks and promises.
Note: examples are using top level await.