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).