{"action":"created","comment":{"url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/comments/26328088","id":26328088,"diff_hunk":"@@ -0,0 +1,601 @@\n+---\n+comments: true\n+date: 2015-02-11 16:10:00\n+layout: post\n+slug: event-sourced-dice-game-part1\n+title: Event-sourced game implementation example - Part 1/3&#58; Getting started\n+summary: Simple (yet complete) example of game implementation using CQRS and event sourcing backed by Akka persistence and RabbitMQ. Part 1/3\n+author: Łukasz Gąsior\n+tags:\n+- Event Sourcing\n+- CQRS\n+- Akka persistence\n+- RabbitMQ\n+- Reactive rabbit\n+- DDD\n+---\n+\n+Hi, in this post series we'll create a really simple (yet complete) event-sourced game. \n+It won't be anything spectacular thus its rules are as simple as:\n+\n+- game creator specifies players taking part\n+- each player, one after another, has an opportunity to roll the dice\n+- each player's opportunity to roll is time limited, if player won't roll within the limit, his opportunity is gone\n+- winners are all players who share the highest rolled number\n+\n+With these simple rules it's barely playable but who cares, at least it's event-sourced!\n+\n+Full source code is available on [GitHub](https://github.com/LukasGasior1/event-sourced-dice-game).\n+\n+Let's go...\n+\n+## 1. Architecture overview\n+![Architecture](https://raw.githubusercontent.com/LukasGasior1/event-sourced-dice-game/master/doc/diagram.png)\n+\n+We'll split our application into three separate modules: game, webappp and statistics.\n+\n+Game is our core module, it's where all business logic lives. It has REST API to handle commands and it publishes events\n+to RabbitMQ. \n+\n+Webapp is an example user interface client consuming our game module. It'll be created using Play Framework, AngularJS and WebSocket.\n+\n+Additionaly, we'll create statistics module that'll collect data from game's events\n+and expose simple REST API to get calculated dice rolls statistics.\n+\n+In this part we'll take a look at game module followed by webapp and statistics in 2nd and 3rd part of this series.\n+\n+## 1. Domain - game's heart\n+We'll begin with implementing main portion of game's logic - our domain. We'll do it using event sourcing technique.\n+\n+Basic idea behind event sourcing is quite simple: an aggregate root (our game) receives (non-persistent) **commands**. \n+\n+As a result it generates persistent **events** that are saved to event store (this is what Akka persistence takes care of). Right after saving, events are **applied** to the aggregate root, changing it's **state** - it's the only way of changing it.\n+\n+Having this in mind, we can restore any past state of an aggregate just by subsequently applying its saved events. On the other hand, it means that if we want to know current state of an aggregate we need to take **all** of it's saved events and apply them ony by one. And yes, you are right - this may be inefficient. \n+\n+Here's where **snapshots** come into play - they are whole state dumps up to certain point in event stream. If we have a snapshot, we can simply set it as our current state and only apply events that were saved after this snapshot's creation. Usually it's useful to take snapshots from time to time (for example every 100 events) but we'll not cover them in this example.\n+\n+Summing up, here are the ingredients that we'll use:\n+\n+- game - contains game rules logic, an aggregate root\n+- commands - game is capable of handling them (\"Roll the dice\" is an example of command)\n+- violations - if command being handled breaks game's rules (for example if player tries to roll while it's not his turn) we indicate a violation\n+- events - they are generated by game when something happens (for example: \"Dice has been rolled\" or \"Game has finished\") - once applied, they change game's state \n+\n+### Game\n+Our game is an event-driven aggregate root. It's immutable and most of its public methods return new copy of itself with some changes.\n+Applied changes are reflected in generated events added to returned game.\n+\n+Here's how our game's interface looks:\n+\n+{% highlight scala %}\n+sealed trait Game {\n+  def handleCommand(command: GameCommand): Either[GameRulesViolation, Game]\n+  def applyEvent: PartialFunction[GameEvent, Game]\n+}\n+\n+case class UninitializedGame(...) extends Game {\n+  def start(players: Seq[PlayerId]): Either[GameRulesViolation, Game]\n+}\n+\n+case class RunningGame(...) extends Game {\n+  def roll(player: PlayerId): Either[GameRulesViolation, Game]\n+  def tickCountdown(): Game\n+}\n+\n+case class FinishedGame(...) extends Game {}\n+{% endhighlight %}\n+[\\[game.scala\\]](https://github.com/LukasGasior1/event-sourced-dice-game/blob/master/game/src/main/scala/lgasior/dicegame/domain/game.scala)\n+\n+As you can see, game can be in one of 3 states:\n+\n+- uninitialized - right after creation\n+- running - once started\n+- finished - after all players roll (or timeout)\n+\n+### Commands\n+There are just two commands that our game needs to handle:\n+\n+{% highlight scala %}\n+sealed trait GameCommand\n+case class StartGame(players: Seq[PlayerId]) extends GameCommand\n+case class RollDice(player: PlayerId) extends GameCommand\n+{% endhighlight %}\n+[\\[command.scala\\]](https://github.com/LukasGasior1/event-sourced-dice-game/blob/master/game/src/main/scala/lgasior/dicegame/domain/command.scala)\n+\n+I decided to make `Game` trait responsible for handling these commands, thats what `handleCommand` method does.\n+It simply dispatches command to corresponding method calls (`start()` or `roll()`).\n+If command cannot be applied in current state (for example `RollDice` in `UninitializedGame`) it returns suitable violation.\n+Some may prefer to have `handleCommand` implemented in each subclass instead of `Game` trait. Either ways is fine.\n+\n+Here's the `handleCommand` method:\n+\n+{% highlight scala %}\n+def handleCommand(command: GameCommand): Either[GameRulesViolation, Game] = command match {\n+  case StartGame(players) => this match {\n+    case ug: UninitializedGame => ug.start(players)\n+    case _ => GameAlreadyStartedViolation\n+  }\n+  case RollDice(player) => this match {\n+    case rg: RunningGame => rg.roll(player)\n+    case _ => GameNotRunningViolation\n+  }\n+}\n+{% endhighlight %}\n+[\\[game.scala\\]](https://github.com/LukasGasior1/event-sourced-dice-game/blob/master/game/src/main/scala/lgasior/dicegame/domain/game.scala#L14)\n+\n+Apart from commands we'll have a method to update current turn countdown (`tickCountdown()`).\n+It'll take care of updating remaining time for player's turn as well as turn timeout.","path":"_posts/11-02-2015-event-sourced-dice-game-part1.md","position":128,"original_position":128,"commit_id":"5bfb76d311012f613a343edd2fd89768d775ff8e","original_commit_id":"5bfb76d311012f613a343edd2fd89768d775ff8e","user":{"login":"szabba","id":820119,"avatar_url":"https://avatars.githubusercontent.com/u/820119?v=3","gravatar_id":"","url":"https://api.github.com/users/szabba","html_url":"https://github.com/szabba","followers_url":"https://api.github.com/users/szabba/followers","following_url":"https://api.github.com/users/szabba/following{/other_user}","gists_url":"https://api.github.com/users/szabba/gists{/gist_id}","starred_url":"https://api.github.com/users/szabba/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/szabba/subscriptions","organizations_url":"https://api.github.com/users/szabba/orgs","repos_url":"https://api.github.com/users/szabba/repos","events_url":"https://api.github.com/users/szabba/events{/privacy}","received_events_url":"https://api.github.com/users/szabba/received_events","type":"User","site_admin":false},"body":"*updating __the time left in a__ player's turn*\r\n\r\n*as __the__ turn timeout.*","created_at":"2015-03-12T18:05:06Z","updated_at":"2015-03-12T18:05:06Z","html_url":"https://github.com/ScalaConsultants/ScalaConsultants.github.io/pull/35#discussion_r26328088","pull_request_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/35","_links":{"self":{"href":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/comments/26328088"},"html":{"href":"https://github.com/ScalaConsultants/ScalaConsultants.github.io/pull/35#discussion_r26328088"},"pull_request":{"href":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/35"}}},"pull_request":{"url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/35","id":29893432,"html_url":"https://github.com/ScalaConsultants/ScalaConsultants.github.io/pull/35","diff_url":"https://github.com/ScalaConsultants/ScalaConsultants.github.io/pull/35.diff","patch_url":"https://github.com/ScalaConsultants/ScalaConsultants.github.io/pull/35.patch","issue_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/issues/35","number":35,"state":"open","locked":false,"title":"CQRS/ES post draft","user":{"login":"LukasGasior1","id":5664242,"avatar_url":"https://avatars.githubusercontent.com/u/5664242?v=3","gravatar_id":"","url":"https://api.github.com/users/LukasGasior1","html_url":"https://github.com/LukasGasior1","followers_url":"https://api.github.com/users/LukasGasior1/followers","following_url":"https://api.github.com/users/LukasGasior1/following{/other_user}","gists_url":"https://api.github.com/users/LukasGasior1/gists{/gist_id}","starred_url":"https://api.github.com/users/LukasGasior1/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/LukasGasior1/subscriptions","organizations_url":"https://api.github.com/users/LukasGasior1/orgs","repos_url":"https://api.github.com/users/LukasGasior1/repos","events_url":"https://api.github.com/users/LukasGasior1/events{/privacy}","received_events_url":"https://api.github.com/users/LukasGasior1/received_events","type":"User","site_admin":false},"body":"+ pygments","created_at":"2015-02-24T12:34:24Z","updated_at":"2015-03-12T18:05:06Z","closed_at":null,"merged_at":null,"merge_commit_sha":"461de0a7084b2816aea1990632557ebfdcac76d3","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/35/commits","review_comments_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/35/comments","review_comment_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/comments{/number}","comments_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/issues/35/comments","statuses_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/statuses/5bfb76d311012f613a343edd2fd89768d775ff8e","head":{"label":"ScalaConsultants:cqrs-game","ref":"cqrs-game","sha":"5bfb76d311012f613a343edd2fd89768d775ff8e","user":{"login":"ScalaConsultants","id":6207831,"avatar_url":"https://avatars.githubusercontent.com/u/6207831?v=3","gravatar_id":"","url":"https://api.github.com/users/ScalaConsultants","html_url":"https://github.com/ScalaConsultants","followers_url":"https://api.github.com/users/ScalaConsultants/followers","following_url":"https://api.github.com/users/ScalaConsultants/following{/other_user}","gists_url":"https://api.github.com/users/ScalaConsultants/gists{/gist_id}","starred_url":"https://api.github.com/users/ScalaConsultants/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ScalaConsultants/subscriptions","organizations_url":"https://api.github.com/users/ScalaConsultants/orgs","repos_url":"https://api.github.com/users/ScalaConsultants/repos","events_url":"https://api.github.com/users/ScalaConsultants/events{/privacy}","received_events_url":"https://api.github.com/users/ScalaConsultants/received_events","type":"Organization","site_admin":false},"repo":{"id":15366666,"name":"ScalaConsultants.github.io","full_name":"ScalaConsultants/ScalaConsultants.github.io","owner":{"login":"ScalaConsultants","id":6207831,"avatar_url":"https://avatars.githubusercontent.com/u/6207831?v=3","gravatar_id":"","url":"https://api.github.com/users/ScalaConsultants","html_url":"https://github.com/ScalaConsultants","followers_url":"https://api.github.com/users/ScalaConsultants/followers","following_url":"https://api.github.com/users/ScalaConsultants/following{/other_user}","gists_url":"https://api.github.com/users/ScalaConsultants/gists{/gist_id}","starred_url":"https://api.github.com/users/ScalaConsultants/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ScalaConsultants/subscriptions","organizations_url":"https://api.github.com/users/ScalaConsultants/orgs","repos_url":"https://api.github.com/users/ScalaConsultants/repos","events_url":"https://api.github.com/users/ScalaConsultants/events{/privacy}","received_events_url":"https://api.github.com/users/ScalaConsultants/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ScalaConsultants/ScalaConsultants.github.io","description":"Scalac blog","fork":false,"url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io","forks_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/forks","keys_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/teams","hooks_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/hooks","issue_events_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/issues/events{/number}","events_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/events","assignees_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/assignees{/user}","branches_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/branches{/branch}","tags_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/tags","blobs_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/git/refs{/sha}","trees_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/statuses/{sha}","languages_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/languages","stargazers_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/stargazers","contributors_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/contributors","subscribers_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/subscribers","subscription_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/subscription","commits_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/commits{/sha}","git_commits_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/git/commits{/sha}","comments_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/comments{/number}","issue_comment_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/issues/comments{/number}","contents_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/contents/{+path}","compare_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/merges","archive_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/downloads","issues_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/issues{/number}","pulls_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls{/number}","milestones_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/milestones{/number}","notifications_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/labels{/name}","releases_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/releases{/id}","created_at":"2013-12-21T22:28:57Z","updated_at":"2015-03-04T18:14:36Z","pushed_at":"2015-03-12T17:11:21Z","git_url":"git://github.com/ScalaConsultants/ScalaConsultants.github.io.git","ssh_url":"git@github.com:ScalaConsultants/ScalaConsultants.github.io.git","clone_url":"https://github.com/ScalaConsultants/ScalaConsultants.github.io.git","svn_url":"https://github.com/ScalaConsultants/ScalaConsultants.github.io","homepage":null,"size":2679,"stargazers_count":0,"watchers_count":0,"language":"HTML","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":8,"mirror_url":null,"open_issues_count":2,"forks":8,"open_issues":2,"watchers":0,"default_branch":"master"}},"base":{"label":"ScalaConsultants:master","ref":"master","sha":"e7894e893d4ea0e78ce84081ccf7a32537de046b","user":{"login":"ScalaConsultants","id":6207831,"avatar_url":"https://avatars.githubusercontent.com/u/6207831?v=3","gravatar_id":"","url":"https://api.github.com/users/ScalaConsultants","html_url":"https://github.com/ScalaConsultants","followers_url":"https://api.github.com/users/ScalaConsultants/followers","following_url":"https://api.github.com/users/ScalaConsultants/following{/other_user}","gists_url":"https://api.github.com/users/ScalaConsultants/gists{/gist_id}","starred_url":"https://api.github.com/users/ScalaConsultants/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ScalaConsultants/subscriptions","organizations_url":"https://api.github.com/users/ScalaConsultants/orgs","repos_url":"https://api.github.com/users/ScalaConsultants/repos","events_url":"https://api.github.com/users/ScalaConsultants/events{/privacy}","received_events_url":"https://api.github.com/users/ScalaConsultants/received_events","type":"Organization","site_admin":false},"repo":{"id":15366666,"name":"ScalaConsultants.github.io","full_name":"ScalaConsultants/ScalaConsultants.github.io","owner":{"login":"ScalaConsultants","id":6207831,"avatar_url":"https://avatars.githubusercontent.com/u/6207831?v=3","gravatar_id":"","url":"https://api.github.com/users/ScalaConsultants","html_url":"https://github.com/ScalaConsultants","followers_url":"https://api.github.com/users/ScalaConsultants/followers","following_url":"https://api.github.com/users/ScalaConsultants/following{/other_user}","gists_url":"https://api.github.com/users/ScalaConsultants/gists{/gist_id}","starred_url":"https://api.github.com/users/ScalaConsultants/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ScalaConsultants/subscriptions","organizations_url":"https://api.github.com/users/ScalaConsultants/orgs","repos_url":"https://api.github.com/users/ScalaConsultants/repos","events_url":"https://api.github.com/users/ScalaConsultants/events{/privacy}","received_events_url":"https://api.github.com/users/ScalaConsultants/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ScalaConsultants/ScalaConsultants.github.io","description":"Scalac blog","fork":false,"url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io","forks_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/forks","keys_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/teams","hooks_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/hooks","issue_events_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/issues/events{/number}","events_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/events","assignees_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/assignees{/user}","branches_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/branches{/branch}","tags_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/tags","blobs_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/git/refs{/sha}","trees_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/statuses/{sha}","languages_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/languages","stargazers_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/stargazers","contributors_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/contributors","subscribers_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/subscribers","subscription_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/subscription","commits_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/commits{/sha}","git_commits_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/git/commits{/sha}","comments_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/comments{/number}","issue_comment_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/issues/comments{/number}","contents_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/contents/{+path}","compare_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/merges","archive_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/downloads","issues_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/issues{/number}","pulls_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls{/number}","milestones_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/milestones{/number}","notifications_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/labels{/name}","releases_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/releases{/id}","created_at":"2013-12-21T22:28:57Z","updated_at":"2015-03-04T18:14:36Z","pushed_at":"2015-03-12T17:11:21Z","git_url":"git://github.com/ScalaConsultants/ScalaConsultants.github.io.git","ssh_url":"git@github.com:ScalaConsultants/ScalaConsultants.github.io.git","clone_url":"https://github.com/ScalaConsultants/ScalaConsultants.github.io.git","svn_url":"https://github.com/ScalaConsultants/ScalaConsultants.github.io","homepage":null,"size":2679,"stargazers_count":0,"watchers_count":0,"language":"HTML","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":8,"mirror_url":null,"open_issues_count":2,"forks":8,"open_issues":2,"watchers":0,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/35"},"html":{"href":"https://github.com/ScalaConsultants/ScalaConsultants.github.io/pull/35"},"issue":{"href":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/issues/35"},"comments":{"href":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/issues/35/comments"},"review_comments":{"href":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/35/comments"},"review_comment":{"href":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/35/commits"},"statuses":{"href":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/statuses/5bfb76d311012f613a343edd2fd89768d775ff8e"}}}}