Skip to content

profjordanov/sports-system

Repository files navigation

JBet

PRs Welcome

Build status

Codacy Badge

Web based sport system to bet for matches, played by teams. As a whole, this project aims to implement most of the AspNetCore-Developer-Roadmap

Data Model

The system holds teams, players, matches, comments, users, bets and votes.

  • Teams have name, nick name (optional), web site (optional), date founded (optional) and a set of players.
  • Players have name, date of birth, height, and may be part of some team or be unemployed.
  • Teams play matches. Each match has home team, away team, date and time and a set of comments.
  • Comments have content (text), date and time and owner user (author).
  • Users have username, email and password (encrypted). Users hold also a set of bets and a set of comments for the matches.
  • Users can bet some money for the home or away team for existing match.
  • Users can vote for a team (give +1) ones.

Features

  1. Domain-Driven Design in Practice a.k.a DDD
  2. REST with HATEOAS by following HAL
  3. Command and Query Responsibility Segregation (CQRS) via MediatR
  4. Functional style command/query handlers via robust option/maybe type Optional

Examples:

// LoginHandler.cs
public Task<Option<JwtView, Error>> Handle(Login command, CancellationToken cancellationToken = default) =>
    ValidateCommand(command).FlatMapAsync(cmd =>
    FindUser(command.Email).FlatMapAsync(user =>
    CheckPassword(user, command.Password).FlatMapAsync(_ =>
    GetExtraClaims(user).MapAsync(async claims =>
    GenerateJwt(user, claims)))));
  1. Event-sourcing via Marten
  2. A complete integration tests suite

Examples:

// AuthControllerTests.cs
[Theory]
[CustomizedAutoData]
public async Task LoginShouldSetProperHttpOnlyCookie(Register register)
{
    // Arrange
    await _authHelper.Register(register);

    var loginCommand = new Login
    {
        Email = register.Email,
        Password = register.Password
    };

    // Act
    var response = await _fixture.ExecuteHttpClientAsync(client =>
        client.PostAsJsonAsync(AuthRoute("login"), loginCommand));

    // Assert
    var token = (await response
            .ShouldDeserializeTo<LoginResource>())
        .TokenString;

    response.Headers.ShouldContain(header =>
        header.Key == "Set-Cookie" &&
        header.Value.Any(x => x.Contains(AuthConstants.Cookies.AuthCookieName) && x.Contains(token)));
}
  1. Real-time communications through SignalR
  2. AutoMapper
  3. EntityFramework Core with PostgreSQL Server and ASP.NET Identity
  4. JWT authentication/authorization
  5. File logging with Serilog
  6. Stylecop
  7. Swagger UI + Fully Documented Controllers
  8. Global Model Errors Handler
  9. Global Environment-Dependent Exception Handler
  10. Thin Controllers

Examples:

// BetsController.cs
/// <summary>
/// Logged-in users can bet for the home team.
/// </summary>
/// <param name="input">HTTP request.</param>
[HttpPost("home-team", Name = nameof(BetForHomeTeam))]
[ProducesResponseType(typeof(UserMatchBetResource), (int)HttpStatusCode.Created)]
public async Task<IActionResult> BetForHomeTeam([FromBody] MatchHomeBetInput input) =>
    (await Mediator.Send(new UserBetForHomeTeam(input, CurrentUserId))
        .MapAsync(_ => ToEmptyResourceAsync<UserMatchBetResource>()))
        .Match(resource => CreatedAtAction(nameof(BetForHomeTeam), resource), Error);
  1. FluentValidation
  2. Neat folder structure
├───client
│   ├───public
│   │   ├───index.html
│   │   └───manifest.json
│   ├───src
│   │   ├───api
│   │   ├───components
│   │   ├───redux
│   │   └───utils
├───server
│   ├───configuration
│   │   ├───analyzers.ruleset
│   │   └───stylecop.json
│   ├───src
│   │   ├───Jbet.Api
│   │   ├───Jbet.Business
│   │   ├───Jbet.Core
│   │   ├───Jbet.Domain
│   │   └───Jbet.Persistence
│   └───tests
│       └───Jbet.Tests

Test Suite

  1. Arrange Act Assert Pattern
  2. xUnit
  3. Autofixture
  4. Moq
  5. Shouldly
  6. FakeItEasy
  7. Respawn

Functionality

  • Anonymous users can register user account by email and password.
  • Anonymous users can login by email and password.
  • Logged-in users can logout.
  • Anonymous users can view the home page, holding the top 3 matches (having most bets) and best 3 teams (most voted).
  • Anonymous users can view all matches (ordered by date, with paging).
  • Logged-in users can view team details (information about the team and its players).
  • Logged-in users can vote for a team (no more than once).
  • Logged-in users can view match details (home team, away team, date and comments).
  • Logged-in users can add comments for given match.
  • Logged-in users can bet for the home or away team.
  • Logged-in users can create a new team and assign players to the new team.