-
Notifications
You must be signed in to change notification settings - Fork 5
Project structuur

In de solution vind je 9 projecten die in 2 categorieën opgedeeld kunnen worden:
- Productiecode -> code die bijdraagt tot de functionaliteit van de Api
- Testcode -> code die automatische testen bevat die de productiecode controleren
De productie code is opgedeeld in 4 projecten a.k.a. lagen:
- Stratego.Api -> Dit project is de eigenlijke webapplicatie. In de Controller folder vind je enkele classes die methoden bevatten die uitgevoerd worden als er een http request binnenkomt op een bepaalde url. Om de minimale vereisten te implementeren is het niet nodig om hier 1 letter code te wijzigen. Het gaat wel handig zijn om hier breakpoints te plaatsen om je applicatie te debuggen.
- Stratego.AppLogic-> Bevat enkele service classes die gebruikt worden door de Controller classes in het Stratego.Api project. Hierin zal je enkele gaten moeten invullen.
- Stratego.Domain -> Bevat de domain classes. Deze classes proberen de logica van een stratego-spel te capteren. Hier zitten de grootste gaten die je zal moeten opvullen.
- Stratego.Infrastructure-> Bevat logica die toelaat om data op te slaan in een database of in geheugen. Voor deze applicatie worden gebruikers (spelers) opgeslagen in een database. De lobby (waiting pool), de kandidaten (de spelers die online zijn en een spel willen spelen) en de lopende games worden in het geheugen bijgehouden. Enkel voor het bijhouden van kandidaten moet je nog enkele gaten invullen. De code voor het bijhouden van spelen en gebruikers krijg je cadeau, maar het is niet slecht om de interne werking van InMemoryGameRepository eens te bestuderen.
De testcode bevat de automatische testen. Hierin mag je niets wijzigen (Als je dit toch doet, dan zullen de testresultaten niet in het Guts-systeem terecht komen). Je kan de testen ook beschouwen als documentatie over de code:
- Uit de naam van een test kan je het gewenst gedrag van de code afleiden
- De feedback die je krijgt van rode testen zal je vaak ook in de juiste richting sturen
De volgende projecten in de solution bevatten testcode:
- Stratego.Api.Tests -> Bevat automatische testen voor de Controller classes in het Stratego.Api project. Deze testen zijn initieel allemaal groen. Aan jullie om dit zo te houden!
- Stratego.AppLogic.tests -> Bevat automatische testen voor de classes in het Stratego.AppLogic project. Gebruik (naast de documentatie) deze testen om te achterhalen hoe je deze classes het best implementeert.
- Stratego.Infrastructure.tests -> Bevat automatische testen voor de classes in het Stratego.Infrastructure project.
- Stratego.Domain.Tests -> Bevat automatische testen voor de classes in het Stratego.Domain project. Gebruik (naast de documentatie) deze testen om te achterhalen hoe je deze classes het best implementeert.
- Stratego.TestTools -> Bevat enkele classes die gebruikt worden door de testprojecten. Dit project mag je volledig negeren.
De startcode bevat ook heel wat documentatie over de code in de vorm van commentaren. Deze commentaren kunnen je zeker ook helpen om de code te begrijpen.
Goed om weten: Soms zie je boven een class definitie het volgende staan:
/// <inheritdoc />
Dit wil zeggen dat je documentatie kan vinden in de interface die de class implementeert. De werking van de WaitingPool class, bijvoorbeeld, is grotendeels gedocumenteerd in de IWaitingPool interface.
Als je de applicatie start, dan word je automatisch geleid naar https://localhost:5001/swagger/index.html.
Hier vind je een uitleg over de verschillende http requesten die je kan sturen naar endpoints van de backend. (Lees eerst even de documentatie over client-server architectuur als je nog niet weet wat er met endpoints bedoeld wordt.) Je kan hier voor elke request zien welke parameters (json) er verwacht worden en welke responses je mag verwachten. Je kan zelfs de API aanspreken via deze user interface.

Wat misschien minder duidelijk is, is welke code er uitgevoerd wordt als er een http request aankomt op een endpoint van de applicatie. Hieronder vind je een overzicht van de methodes die worden uitgevoerd:
| Http request | C# methode | C# class | Omschrijving |
|---|---|---|---|
| POST https://localhost:5001/api/authentication/register | Register | AuthenticationController | Nieuwe gebruiker registreren |
| POST https://localhost:5001/api/authentication/token | CreateToken | AuthenticationController | Een bearer token vragen |
| POST https://localhost:5001/api/waitingpool/join | Join | WaitingPoolController | De aangemelde gebruiker toevoegen aan de lijst van wachtende kandidaten |
| POST https://localhost:5001/api/waitingpool/leave | Leave | WaitingPoolController | De aangemelde gebruiker verwijderen uit de lijst van wachtende kandidaten |
| GET https://localhost:5001/watingpool/candidates/me | GetOwnCandidate | WaitingPoolController | Informatie over de kandidatuur van de aangemelde gebruiker |
| GET https://localhost:5001/api/game/{id} | GetPlayerGame | GameController | Informatie van je spel |
| GET https://localhost:5001/api/game/{id}/board | GetBoard | GameController | Informatie ophalen van het spelbord |
| POST https://localhost:5001/api/game/{id}/position-piece | PositionPiece | GameController | Een speelstuk positioneren in setup fase van het spel |
| POST https://localhost:5001/api/game/{id}/ready | SetPlayerReady | GameController | Markeert speler als 'ready'. Moet gebeuren nadat elke speler alle speelstukken heeft geplaatst zodat spel kan starten. |
| POST https://localhost:5001/api/game/{id}/move-piece | MovePiece | GameController | Verplaatst speelstuk wanneer spel gestart is. |
| GET https://localhost:5001/api/game/{id}/last-move | GetLastMove | GameController | Informatie over laatste zet. |
| GET https://localhost:5001/api/home/ping | Ping | HomeController | Endpoint om te controleren of de applicatie nog draait |
De betekenis van elk end point wordt meer toegelicht in de commentaren die in de code staan. Je hoeft (voor de minimale vereisten) geen code te wijzigen in dit project. Wel is het aan te raden om breakpoints te plaatsen in de methodes in bovenstaande tabel om zo all debuggend de flow van de code te analyseren.
Voor de meeste end points moet de gebruiker zich authenticeren door een (bearer) token mee te sturen in de Authorization header van de http request. Dit token kan je verkrijgen via het end point https://localhost:5001/api/authentication/token. (dit kan je dus eventueel opvragen bij login)
De http header ziet er als volgt uit:
Authorization: Bearer {theToken}
De class WaitingPool is de class die door de WaitingController class in het Api project gebruikt wordt om de kandidaten die een spel willen spelen, te beheren. De class coordineert de logica rond:
- het toevoegen van een kandidaat aan de wachtruimte (de pool)
- het (automatisch) vinden van een tegenspeler
- het verlaten van de pool
De WaitingPool class coördineert. Dat wil zeggen dat hij veel verantwoordelijkheid doorschuift naar andere classes (in het Stratego.Infrastructure project en Stratego.Domain project). De WaitingPool doet alles met 4 instanties van objecten die worden doorgegeven via de constructor:
- een instantie van een class die IGameCandidateFactory implementeert. Hiermee kan een GameCandidate aangemaakt worden die dan later in de pool gestopt kan worden.
- een instantie van een class die IGameCandidateRepository implementeert. Hiermee kan een nieuwe kandidaat opgeslagen worden in het geheugen en terug opgehaald worden. De concrete instantie zal van het type Stratego.Infrastructure.Storage.InMemoryGameCandidateRepository zijn.
- een instantie van een class die IGameCandidateMatcher implementeert. Hiermee kan een match gezocht worden voor een kandidaat in de pool. De concrete instantie zal van het type Stratego.AppLogic.BasicGameCandidateMatcher zijn.
- een instantie van een class die IGameService implementeert. Hiermee kan een spel aangemaakt worden als een kandidaat een match (tegenspeler) heeft gevonden. De concrete instatie zal van het type Stratego.AppLocic.GameService zijn.
De gegeven code zorgt er al voor dat de juiste instanties van de juiste classes doorgegeven worden aan de constructor. Jij moet deze instanties nu nog correct gebruiken. Laat je leiden door de automatische testen...
De class GameService is de class die door de controller classes in het Api project gebruikt wordt om bewerkingen rond een spel (Game) te doen. De class coordineert de logica rond:
- het aanmaken van een Game en deze opslaan in het geheugen
- het ophalen van het spelbord van een Game
- het ophalen van het spel, gezien door de ogen van de speler
- het positioneren van een speelstuk
- een speler als 'ready' markeren
- een speler een speelstuk laten verplaatsen
- de laatste move ophalen
De GameService class coördineert. Dat wil zeggen dat hij veel verantwoordelijkheid doorschuift naar andere classes (in het Stratego.Infrastructure project en Stratego.Domain project). De GameService doet alles met 5 instanties van objecten die worden doorgegeven via de constructor:
- een instantie van een class die IGameFactory implementeert. Hiermee kan een Game kan aangemaakt worden. De concrete instantie zal van het type Stratego.Domain.GameFactory zijn.
- een instantie van een class die IGameRepository implementeert. Hiermee kan een nieuw spel opgeslagen worden in het geheugen en terug opgehaald worden. De concrete instantie zal van het type Stratego.Infrastructure.Storage.InMemoryGameRepository zijn.
- een instantie van een class die IBoardDtoFactory implementeert. Hiermee kan je de informatie over het Board (Speelbord) construeren.
- een instantie van een class die IPlayerGameDtoFactory implementeert. Hiermee kan je informatie construeren van het spel gezien door de ogen van een speler. Een player mag niet alle informatie van een Game te zien krijgen (bijvoorbeeld de sterkte van de speelstukken van de tegenstander). Daarom wordt een Game eerst omgezet naar een PlayerGameDto object vooraleer we het over het netwerk naar de speler sturen.
- een instantie van een class die IUserRepository implementeert. Deze wordt enkel gebruikt indien je de extra rond rankings implementeert.
De gegeven code zorgt er al voor dat de juiste instanties van de juiste classes doorgegeven worden aan de constructor. Jij moet deze instanties nu nog correct gebruiken. Laat je leiden door de automatische testen...
Bevat info over het type spel dat je wil spelen. Je kan enkel gematcht worden met tegenspelers met dezelfde gamesettings.
In de Dto folder vind je de PlayerGameDto class die gegevens over een Game bevat door de bril van een Player. PlayerGameDto bevat de volgende informatie:
- Id: unieke identifier (Guid) van de Game. Zoek zelf eens op wat een Guid betekent en hoe je er mee werkt in C#.
- OwnPlayerIsReady: geeft aan of er gestart kan worden met spelen (als alle speelstukken gepositioneerd zijn en de speler zichzelf gemarkeerd heeft als 'ready').
- OwnColorIsRed: geeft aan of de speler een rode speler is of niet.
- OwnLivingPieces: lijst van speelstukken die nog in leven zijn.
- OwnFallenPieces: lijst van speelstukken die niet meer in leven zijn.
- OwnArmyIsDefeated: geeft aan of eigen leger is verslagen of niet (vlag gevangen/ geen speelstukken meer die kunnen verplaatst worden)
- OpponentIsReady: geeft aan of tegenstander klaar is om te spelen (als alle speelstukken gepositioneerd zijn en de speler zichzelf gemarkeerd heeft als 'ready').
- OpponentLivingPieceCoordinates: de coördinaten van de speelstukken van de tegenspeler die nog in leven zijn.
- OpponentFallenPieces: lijst van speelstukken van de tegenspeler die overwonnen zijn.
- OpponentArmyIsDefeated: geeft aan of het leger van de tegenstander is verslagen of niet (vlag van tegenstander gevangen / tegenspeler heeft geen sppeelstukken meer die kunnen verplaatst worden).
- IsStarted: geeft aan of beide spelers zich gemarkeerd hebben als 'ready'.
- IsYourTurn: geeft aan of de speler een speelstuk mag verplaatsen.
- IsOver: geeft aan of een van de 2 legers is verslagen.
Aan de classes PlayerGameDto en BoardDto hoef je niets meer te doen. Waar je wel nog aan moet werken zijn de bijhorende Factory classes: PlayerGameDtoFactory en BoardDtoFactory. De factory classes bevatten de logica die nodig is om informatie over een Game om te zetten naar informatie die een speler mag zien. Laat je leiden door de automatische testen...
In dit project hoef je enkel code te wijzigen in de class InMemoryGameCandidateRepository in de Storage folder. Bestudeer best ook eens de gegeven code in die class. In de InMemoryGameCandidateRepository kan je zien hoe gedurende de levensduur van de web applicatie de verschillende kandidaten in geheugen worden bijgehouden.
Bestudeer ook zeker eens hoe de InMemoryGameRepository werkt.
De geregistreerde gebruikers worden in een database opgeslagen. Deze database kan je als volgt vinden:
- Open het venster Sql Server Object Explorer in Visual Studio

- Maak een connectie met de lokale database server:
- Klik op het +-icoon
- Open Local
- Kies voor MSSQLLocalDb
- Klik op Connect zonder iets te wijzigen

- Bij het starten van de Api wordt er automatisch een database StrategoDb aangemaakt. Deze zou je moeten terugvinden onder (localdb)\MSSQLLocalDB -> Databases.
Om de gebruikers te bekijken die in de databank zitten doe je het volgende:
- Ga naar StrategoDb -> Tables
- Klik rechts op dbo.AspNetUsers
- Klik op View Data

Als je met een schone lei terug wil starten dan kan je de database wissen en opnieuw aanmaken als volgt:
- Klik rechts op StrategoDb
- Klik op Delete
- Vink Close existing connections aan en klik op Ok
- Door de Api nu opnieuw te starten zal er automatisch een nieuwe lege databank aangemaakt worden.
De classes in dit project proberen de logica van een stratego-spel te capteren. Het gaat hier om een heel aantal klassen. Daarom zijn er ook 2 subdomeinen:
- ArmyDomain: classes die een leger modelleren (class voor het leger + elk van de speelstukken)
- BoardDomain: classes die een spelbord modelleren (Board, BoardCoordinate, BoardSquare and Move)
Er zijn ook enkele hulp classes die niet tot een specifiek domein horen:
- User: een (menselijke) geregistreerde gebruiker. Je hoeft niets te wijzigen in deze code.
- DataNotFoundException: exceptie die gegooid wordt als er iets niet gevonden kan worden (bijvoorbeeld een Game met een bepaalde id.)
- Result: een class waarmee je een resultaat kan terug geven die een reden bevat als het is mis gegegaan. Ook deze code krijg je cadeau.
- ExpiringDictionary: Dictionary waarin de items een bepaalde levensduur hebben.
In het domain project zijn veel classes aanwezig. Waar moet je nu beginnen?
- Werk feature per feature.
- Traceer de flow van de code vanuit het API project. Plaats een breakpoint in de juiste controller methode en gebruik debugging technieken om dieper en dieper in de code te stappen.
- Vervolledig de methodes / properties die je tegen komt tijdens het debuggen. Laat je leiden door de automatische testen. Methodes en properties die je niet nodig hebt voor de huidige feature laat je voorlopig links liggen.
- Als de automatische testen aangeven dat een bepaalde methode / property van een andere class nog niet geïmplementeerd is dan implementeer je die eerst.
-
Zorg dat je de gegeven code ook begrijpt. Neem de tijd om de achterliggende theorie te bestuderen. Als je weet wat je aan het doen bent dan zal je code veel beter zijn en zal het ook aangenamer zijn om te programmeren.
- Als er in een class parameter van een interface type wordt doorgegeven in een constructor, dan ga je ook de class die de interface implementeert moeten aanvullen om de feature aan het werken te krijgen. Tip: als je rechts klikt op een interface type en je selecteert Go to implementation dan kom je uit op de class die de interface implementeert.
Project 1TIN - Stratego - Terug naar de wiki startpagina