Giter VIP home page Giter VIP logo

kylemcmaster / payroll-processor Goto Github PK

View Code? Open in Web Editor NEW
121.0 8.0 23.0 3.41 MB

A smorgasbord of modern .NET tech written with functional and asynchronous patterns

License: MIT License

HTML 4.69% TypeScript 22.51% JavaScript 1.08% C# 60.74% Dockerfile 0.34% SCSS 0.92% Bicep 2.14% Vue 6.67% CSS 0.90%
angular dotnet-core akita functional-programming azure-functions clean-architecture scss cosmosdb vertical-slice-architecture bootstrap

payroll-processor's Introduction

Payroll Processor

Sample HRIS application where a list of employees and their payroll information would be available in report format.

Twitter Follow Twitter Follow

Status of this Repository

This project was started as an exploration into various technologies and approaches that the contributors had been meaning to try but weren't able to exercise in their employer's production code. The effort and success this project achieved was a labor of love and will forever hold fond memories in my (@KyleMcMaster's) heart as a catalyst for learning and growth that had an impactful experience on my career. Since 2022 or so, development on this project has become stale and the primary contributors have all taken interest in other technologies and problems that aren't represented within the space of this sample application. You can follow my latest development efforts over at FShopOnWeb I hope this code can continue to be a reference for anyone interested in the patterns and principles applied here.

Build status

Api and Functions

dotnet core - build & test

Client

.github/workflows/policy-npm.yml Styled with Prettier

Motivation

This project was created to explore a variety of technologies, patterns, and frameworks in a sandbox style environment. The fictional domain of the application is designed be restrictive enough to mimic a real world application while also allowing creative and technical freedom for the developers involved.

Areas of interest:

  • Functional Programming
  • Automated Tests
  • Basics of Angular, TypeScript, Rxjs, and Bootstrap
  • Synergy that exists between Azure Functions and .NET Core Web APIs
  • Event Sourcing and CQRS
  • Having fun!

Roadmap

MVP

  • Employee and Payroll CRUD functionality representing a simple workflow for the domain
  • Administration screen for seeding data

Future Enhancements

  • Analytics like payroll totals by department, risk, and time
  • Integrate Cosmos Db change feed
  • Handle poison queue messages
  • Add helpful notifications from SignalR messages
  • Configure Tye app orchestrations

Contributors โœจ

Thanks goes to these wonderful people (emoji key):


Kyle McMaster

๐ŸŽจ ๐Ÿ’ป โš ๏ธ

Sean G. Wright

๐ŸŽจ ๐Ÿ’ป ๐Ÿ‘€

Justin Conklin

๐Ÿ‘€ ๐Ÿ’ป

This project follows the all-contributors specification. Contributions of any kind welcome!

Star History โญ

Star History Chart

Project Structure

api/ - .NET API and Azure Functions backend

client/ - Main view of the application built with Angular and Boostrap

vue-client/ - Alternate view of the Admin page built with Vue3 and Tailwind CSS

References

Akita-Demo

Build / Run

This project uses VS Code Multi-root Workspaces. For the best developer experience, open the workspace directly with VS Code (code payroll-processor.code-workspace) or open the root of the repository in VS Code (code .) and when prompted, open the workspace.

API

The API solution (PayrollProcessor.sln) is set up as the default solution for Omnisharp, and is loaded as soon as the VS Code workspace is opened.

All of the backend .NET code is found in the /api folder.

This solution contains 2 applications PayrollProcessor.Functions.Api and PayrollProcess.Web.Api This solution also contains multiple shared libraries and test projects.

There are VS Code tasks (Clean, Build, Test) at the solution and the individual application project level.

PayrollProcess.Web.Api

Currently there are no application secrets or app settings to customize for the Web API. However, settings for the application can be found in appsettings.Development.json and appsettings.json.

To run the Web API run the following launch configuration (Debug: Select and Start Debugging)

  • API: Run and Attach (Debug)

The application will start and listen for requests on http://localhost:5000.

PayrollProcessor.Functions.Api

  • Ensure the Azure Functions VS Code extension is installed.

  • Run "Azure Functions: Install or Update Azure Functions Core Tools" from the command palette.

  • Update Powershell security policy

  • Start the Azure Storage Emulator and Azure Cosmos Db Emulator (see: Data Storage below)

  • Copy

    api/PayrollProcessor.Functions.Api/local.settings.json.sample

    to

    api/PayrollProcessor.Functions.Api/local.settings.json

  • F5 or run from the VS Code Debug drop down "Function: Run & Attach (Debug)"

  • Optional: Run any of the following tasks (Task: Run Task)

    • Function: Test
    • Function: Build (Debug)
  • Optional: Run any of the following launch configurations (Debug: Select and Start Debugging)

    • Function: Run & Attach (Debug)

The application will listen for requests on http://localhost:7071.

Angular Client

  • Copy

    client/src/environments/environment.local.sample.ts

    to

    client/src/environments/environment.local.ts

  • Run the "Client: Serve" VS Code task (this will install packages and start serving the app)

Data Storage

The project currently stores data in Azure Table Storage, which can be simulated locally using the Azurite.

The locally stored data can be viewed using the Azure Storage Explorer.

This project also uses the Azure Cosmos Db Emulator which can be downloaded at https://aka.ms/cosmosdb-emulator

Note: There is a Linux/MacOS emulator available as well

Data Initialization / Seeding

Create Collections / Queues

For performing any of the following operations ensure the following:

  • The Azure Storage Emulator & Azure Cosmos DB Emulator are running
  • The functions API project (see: PayrollProcessor.Functions.Api above) is running

To initialize the data storage structure (a few tables and a queue):

  • Make a POST request to http://localhost:7071/api/resources/
  • Optional: Use Create Resources request in /docs/PayrollProcessor.postman_collection.json Postman collection

The creation process will skip any resources that already exist.

Create Seed Data

There is also an endpoint to initialize randomly generated data in the data storage:

  • Make a POST request to http://localhost:7071/api/resources/data
    • There are 2 optional query parameters
      • employeesCount: Sets the number of employees created by the request
      • payrollsMaxCount: Sets the maximum number of payrolls created for each employee (random value 1-max)
  • Optional: Use Create Seed Data request in /docs/PayrollProcessor.postman_collection.json Postman collection

Reset Collections / Queues and Data

Finally, there's an endpoint to clear/reset all the data currently in the app. This can be useful if you are changing schemas and don't want to write migrations.

  • Make a DELETE request to http://localhost:7071/api/resources
  • Optional: Use Delete Resources request in /docs/PayrollProcessor.postman_collection.json Postman collection

payroll-processor's People

Contributors

allcontributors[bot] avatar dependabot[bot] avatar kylemcmaster avatar seangwright avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

payroll-processor's Issues

Idempotency issue in EmployeePayrollUpdatesQueue function

Description
The EmployeePayrollUpdatesQueue function processes EmployeePayrollCreation and EmployeePayrollUpdate events in the queue. However, there is a potential idempotency issue that might cause duplicate processing of the events if the function is re-invoked with the same input.

Steps to Reproduce

  1. Send an event to the AppResources.Queue.EmployeePayrollUpdates queue to trigger the function.
  2. Have the function processing the event, updating or creating department payroll records.
  3. Simulate an error after the HandleEmployeePayrollCreation (similar if you want to test api call but before the function returns for the function to fail.
  4. Let the function automatically retry with the same message.

Expected Behavior
The EmployeePayrollUpdatesQueue function should be idempotent, ensuring that the same event is not processed multiple times, regardless of any exceptions, errors, or retries. Specifically in this case, we should not be updating or creating payroll records more than once.

Actual Behavior
If an error occurs during the execution of the function (as describe above), it may cause the function to be retried with the same input, leading to duplicate processing of the events and possibly updating or creating duplicate department payroll records.

Suggested fix
To ensure idempotency, implement a mechanism to track the state of the processed input message. This can be done by using Azure Table Storage or another storage service to store a unique identifier for each processed message. Before processing the input message, check if the unique identifier exists in the storage. If it does, skip processing the message, and if it doesn't, process the message and store the unique identifier in the storage. Additionally, consider implementing error handling and logging mechanisms to capture and handle any errors that occur during the processing of the events.

Thank you for your contribution to the Github community and I really appreciate your effort in following through this issue.

Enhancement: Create more flexible environment configurations

We could "merge" defaults with local settings if we wanted, by rewriting this file as:

import { Environment } from './environment-types';
import { environment as envSource } from './environment';

export const environment: Environment = {
  ...envSource,
  production: false,
  domain: 'http://localhost:7071',
};

That way we could put the defaults in environment.ts and anything not explicitly overwritten in environment.local.ts would flow through from there.

I've never done this on a project before, but I have wondered if it would be beneficial...

If we did that, there might be better names for the files, like:

environment.local.user.sample.ts - user specific local sample (committed)
environment.local.user.ts - user specific local configuration copied from environment.local.user.sample.ts) (gitignored) environmemt.local.ts` - default local (committed)

Thoughts?

Originally posted by @seangwright in https://github.com/KyleMcMaster/payroll-processor/pull/15/files

Suggestion: Change `ICommandHandler` type to return `TryAsync` instead of `TryOptionAsync`

The TryOptionAsync type is good at representing an async operation that can result in Success/Fail and in the Success case there is a Some/None value.

This makes sense for our queries because the query itself could succeed or fail (throw exception) and the query itself could return the item(s) or nothing, if they were not found.

However, commands typically don't return anything (except maybe an identifier from a completed operation) and they aren't necessarily looking for anything in particular. If an item that is needed for a command can't be found, that command is probably best considered to be failed.

In summary, we should consider changing ICommandHandler<TCommand> to have a Execute() method with the signature

TryAsync<Unit> Execute(TCommand command, CancellationToken token);

Update Angular Client NPM packages

Update Angular Client NPM packages to latest and verify they work as intended, understandable if not all packages are able to be updated due to dependency hell.

Acceptance Criteria:

  • Packages are updates to latest combination that works with given Node constraints
  • Angular Client builds and displays pages matching current state
  • Angular Client displays data from API

Investigate best logging approaches for LanguageExt integration

LGTM, really exercising the logging ๐Ÿ˜„

@KyleMcMaster Ya. hahah. Those bugs a few PRs back really burned me when I couldn't tell what was failing in the Functions API.

I want to look at alternatives for logging when using LanguageExt. My C#/OO approach would be decoration via DI, but I'm not sure if that's the best 'functional' approach?

Maybe I'll create an IQueryHandler and ICommandHandler logging decorator so we can get insight on all commands and queries.

Originally posted by @seangwright in #42 (comment)

Consider adding a license

Just for completeness' sake, you might consider applying an appropriate open source license to let people know it's okay to borrow some of these patterns, such as the AutoFixture/NSubstitute setup in PayrollProcessor.Tests.

I generally like MIT license, and it's what the dotnet runtime uses.

Update Vue Client NPM Packages

Update Vue Client NPM packages to latest and verify they work as intended.

Acceptance Criteria:

  • Packages are updates to latest combination that works with given Node constraints
  • Vue Client builds and displays page
  • Vue Client displays data from API

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.