github.com/jbendotnet/noms@v0.0.0-20190904222105-c43e4293ea92/doc/decent/quickstart.md (about) 1 [Home](../../README.md) » [Use Cases](../../README.md#use-cases) » **Decentralized** » 2 3 [About](about.md) | **Quickstart** | [Architectures](architectures.md) | [P2P Chat Demo](demo-p2p-chat.md) | [IPFS Chat Demo](demo-ipfs-chat.md) 4 <br><br> 5 # How to Use Noms in a Decentralized App 6 7 If you’d like to use noms in your project we’d love to hear from you: 8 drop us an email ([noms@attic.io](mailto:noms@attic.io)) or send us a 9 message in slack ([slack.noms.io](http://slack.noms.io)). 10 11 The steps you’ll need to take are: 12 13 1. Decide how you’ll model your problem using noms’ datatypes: boolean, 14 number, string, blob, map, list, set, structs, ref, and 15 union. (Note: if you are interested in using CRDTs as an alternative 16 to classic datatypes please let us know.) 17 2. Consider... 18 * How peers will discover each other 19 * How peers will notify each other of changes 20 * How and when they will pull changes, and 21 * What potential there is for conflicting changes. Consider modeling 22 your problem so that changes commute in order to make merging 23 easier. 24 25 In our [p2p sample](https://github.com/attic-labs/noms/blob/master/doc/decent/demo-p2p-chat.md) application, all peers periodically broadcast their HEAD on a known channel using [IPFS pubsub](https://ipfs.io/blog/25-pubsub/), pull each others' changes immediately, and avoid conflicts by using operations that can be resolved with Noms' built in merge policies. 26 27 This is basically the simplest possible approach, but lots of options are possible. For example, an alternate approach for discoverability could be to keep a registry of all participating nodes in a blockchain (e.g., by storing them in an Ethereum smart contract). One could store either the current HEAD of each node (updated whenever the node changes state), or just an IPNS name that the node is writing to. 28 29 As an example of changes that commute consider modeling a stream 30 of chat messages. Appending messages from both parties to a list 31 is not commutative; the result depends on the order in which 32 messages are added to the list. An example of a commutative 33 strategy is adding the messages to a `Map` keyed by 34 `Struct{sender, ordinal}`: the resulting `Map` is the same no 35 matter what order messages are added. 36 37 3. Vendor the code into your project. 38 4. Decide which type of storage you'd like to use: memory (convenient for playing around), disk, IPFS, or S3. (If you want to implement a store on top of another type of storage that's possible too; email us or reach out on slack and we can help.) 39 5. Set up and instantiate a database for your storage. Generally, you use the spec package to parse a [dataset spec](https://github.com/attic-labs/noms/blob/master/doc/spelling.md) like `mem::mydataset` which you can then ask for [`Database`](https://github.com/attic-labs/noms/blob/master/go/datas/database.go) and [`Dataset`](https://github.com/attic-labs/noms/blob/master/go/datas/dataset.go). 40 * **Memory**: no setup required, just instantiate it: 41 42 ```go 43 sp := spec.ForDataset("mem::test") // Dataset name is "test" 44 ``` 45 46 * **Disk**: identify a directory for storage, say `/path/to/chunks`, and then instantiate: 47 48 ```go 49 sp := spec.ForDataset("/path/to/chunks::test") // Dataset name is "test" 50 ``` 51 52 * **IPFS**: identify an IPFS node by directory. If an IPFS node doesn't exist at that directory, one will be created: 53 54 ```go 55 sp := spec.ForDataset("ipfs:/path/to/ipfs_repo::test") // Dataset name is "test" 56 ``` 57 58 * **S3**: Follow the [S3 setup instructions](https://github.com/attic-labs/noms/blob/master/go/nbs/NBS-on-AWS.md) then instantiate a database and dataset: 59 60 ```go 61 sess := session.Must(session.NewSession(aws.NewConfig().WithRegion("us-west-2"))) 62 store := nbs.NewAWSStore("dynamo-table", "store-name", "s3-bucket", s3.New(sess), dynamodb.New(sess), 1<<28)) 63 database := datas.NewDatabase(store) 64 dataset := database.GetDataset("aws://dynamo-table:s3-bucket/store-name::test") // Dataset name is "test" 65 ``` 66 67 7. Implement using the [Go API](https://github.com/attic-labs/noms/blob/master/doc/go-tour.md). If you're just playing around you could try something like this: 68 69 ```go 70 package main 71 72 import ( 73 "fmt" 74 "os" 75 76 "github.com/attic-labs/noms/go/spec" 77 "github.com/attic-labs/noms/go/types" 78 ) 79 80 // Usage: quickstart /path/to/store::ds 81 func main() { 82 sp, err := spec.ForDataset(os.Args[1]) 83 if err != nil { 84 fmt.Fprintf(os.Stderr, "Unable to parse spec: %s, error: %s\n", sp, err) 85 os.Exit(1) 86 } 87 defer sp.Close() 88 89 db := sp.GetDatabase() 90 if headValue, ok := sp.GetDataset().MaybeHeadValue(); !ok { 91 data := types.NewList(sp.GetDatabase(), 92 newPerson("Rickon", true), 93 newPerson("Bran", true), 94 newPerson("Arya", false), 95 newPerson("Sansa", false), 96 ) 97 98 fmt.Fprintf(os.Stdout, "data type: %v\n", types.TypeOf(data).Describe()) 99 _, err = db.CommitValue(sp.GetDataset(), data) 100 if err != nil { 101 fmt.Fprint(os.Stderr, "Error commiting: %s\n", err) 102 os.Exit(1) 103 } 104 } else { 105 // type assertion to convert Head to List 106 personList := headValue.(types.List) 107 // type assertion to convert List Value to Struct 108 personStruct := personList.Get(0).(types.Struct) 109 // prints: Rickon 110 fmt.Fprintf(os.Stdout, "given: %v\n", personStruct.Get("given")) 111 } 112 } 113 114 func newPerson(givenName string, male bool) types.Struct { 115 return types.NewStruct("Person", types.StructData{ 116 "given": types.String(givenName), 117 "male": types.Bool(male), 118 }) 119 } 120 ``` 121 122 8. You can inspect data that you've committed via the [noms command-line interface](https://github.com/attic-labs/noms/blob/master/doc/cli-tour.md). For example: 123 124 ```shell 125 noms log /path/to/store::ds 126 noms show /path/to/store::ds 127 ``` 128 129 > Note that Memory tables won't be inspectable because they exist only in the memory of the process that created them. 130 131 9. Implement pull and merge. The [pull API](../../go/datas/pull.go) is used pull changes from a peer and the [merge API](../../go/merge/) is used to merge changes before commit. There's an [example of merging in the IPFS-based-chat sample 132 app](https://github.com/attic-labs/noms/blob/master/samples/go/ipfs-chat/pubsub.go).