Skip to content

A social media web application allows user to follow other users, publish new posts, comment on published posts, like posts or comments. Users also has their timelines according to the followed users, trend page that shows the trend posts, search operation to find another users, get mailed on account creation or notifications.

Notifications You must be signed in to change notification settings

eneskzlcn/softdare

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

About Project

This is a social media project that users can post their writings, follow other users, comment on other users posts, like other users posts or comments.

The screenshots of the project can be found:

Home Page

Login

User Profile

Post Details

Trends

Local Setup To Run

You need the docker installed on your machine to run the application.

  • docker compose up -d to start rabbitmq and postgres database services.
  • go mod tidy to install all needed dependencies and a cleanup for not needed ones...
  • make migrate-tables to create needed tables on postgres database
  • make build to build the application and make run to start. or you can directly use make start to build first then run.

Additional Information About Local Setup

  • You can find all the database schema under postgres/migration/schema.sql. If you want to contribute or use for your own purposes, you can simply add the tables you want to create to schema.sql and the command make migrate-tables will rebuild all the tables for you after you clean up the database with the command make drop-tables (the thing you need to remember, you should add your new table to postgres/migration/drop.sql too to clean up correctly for next make drop-tables commands.).

  • If you want to clean up all the tables in database and restart again you can call make drop-tables to clean up the database , and make migrate-tables to create the tables again.

  • If you want to consume one more queue from rabbitmq, you need to add the queue name to the config files which is under the .dev/ folder. After you add that queue name to the config file, the program will create the queue automatically when it restarts.

  • If you want to add one more consumer for a queue, you can find the consumers under internal/client/queue. You can add a consumer function like the others I write and provide a consume operation for your purposes.

  • You should not change the directory of gohtml templates. Because there is an embed usage to read that template which should exactly be in the directory that embed expects(or you should change the directory that embed reads from internal/web/template.go too.)

  • If you want to add a new page(new .gohtml) you should put it to the internal/web/templates if it is not a reusable or layout component(that ones go to the include/ directory under the templates directory.) and then you should name your template by registering it to the map sits on internal/web/handler.go (in the init function of Handler struct)

  • If you want to add a new feature starting from web handler and goes to the repository then you should register your web handler functions to the Handler struct defined under internal/web/ directory and better to keep in different file(you can look at my examples like profile, post ,home or etc.), and register service functions to the Service struct in the internal/service and better to keep different file, and register your data access layer functions to Repository struct in the internal/repository and better to keep in different file. Do not forget to register your web handler to the Handler by adding your handler with its route and method to the RegisterHandlers function of the Handler struct which is in internal/web/handler.go

Local Setup For Tests

  • go mod tidy to install all needed dependencies and a cleanup for not needed ones...
  • make clean to clear all additional files.
  • make generate-mocks to generate all needed mock to run tests.
  • make test to run all written tests.

Architectural Decisions

This project teach me a lot of on how you need to choose an architecture for your application. I was generally using DDD approach to create backend applications which recommends that you need to keep inside the things that domain consist of. An example if you are creating an order domain, then all structures, functionalities, tests, business and database access operations should be in the package of that domain.

So first, I tried to make all the domains like post, login, comment, user etc. seperated and independent of each other. After I structured the project like that, I enjoyed and thought to gain a victory about that. Then when time comes to the creating new features on that architecture things started to get more complicated. Every need were causing a dependency between domains/subdomains. The most great example was the entities. I thought any way to protect that structure so I see that it will be costly. So I decided to move all the entities out of the domains and all the domains can reach them without creating a dependency between domains. The folder structure were like;

  • entities/
  • login/
  • post/

After this change, I was comfortable to reach the entities from anywhere and it was easy to add or remove an entity for new feature purposes.

Working on new things obligate me to repeat the implementation of same functions just to keep the domains independent. An example of that is a data access or business function one domain has is also used by another domain. But you can not pass directly to maintain independence.

I googled about that and see the architecture generally used by Java or C# backend programmers named n-Layered Architecture. I realised that the architecture I need to use in a monolithic web application should be that. Because there is no domain specific abstractions so that you can make a folder/package structure like;

   - entity/
   - services/
   - repository/
   - api/
   - web/
   - mobile/
   ...

At last, I chose that architecture as a project architecture and everything become more comfortable, flexible and easier.

As a result, I tried to feel like a startup company that must keep things faster to deploy their app. Generally they prefer monolithic way to deploy the first release as much as faster and then seperated the system to the little services when needed( microservices ). So I think that making all the domains seperated even doing a monolithic application effect the process after first release, so it will be easier to decouple the services as independent microservices. But I learned that making a monolithic application in that way is very hard and painful.

Caching

I use the caching to cache trends data which always going to be same for a while. Normally, trends can change anytime. But, keeping the trends in cache for a while like 1 minutes, will not affect the correctness too much instead it will provide faster results for any user want to see the trends.

Configs

You can find the configuration filed under .dev/ folder for any environment(I have not a production environment yet). After the application starts LoadConfig functions in the internal/config package reads related yaml configs from .dev/ and all configuration variables become a structured data that you pass in any related service that needs an extra configuration properties.

Secrets

Secrets are the same with configs but the things I need to keep private. An example of that is mail service configuration properties which needs my email and its password to send an email.

Mail Service

Mail service sends mail to the users when you needed. When you look at the mail service you notice that it takes all the configuration properties like username, host, port and password from configs (which is a secret config). For security, I kept that configurations as secret and do not push it to my remote repository. If you want to configure it with your own credentials you have two different way you can follow. In first way, which you do not need to touch any path specification for your secrets, you can simply create a file named secrets.yaml to directory named .secrets which lives in project directory ($project_directory/.secrets/secrets.yaml). Then the other parts going to get handled by current code. In the second way, you can place your secrets anywhere you want on one condition that you need to change the path and file name with yours in LoadConfig[config.Secret] functions from cmd/main.go

External Modules Used In Project

  • html/templates built-in module as a html parser and renderer technology.
  • go.uber.org/zap logger module to handle logging operations.
  • go-ozzo/ozzo-validation validation module to validate special structures and inputs.
  • /rs/xid xid module to generate xid for database entities.
  • spf13/viper configuration module to handle and structure environment specific configurations.
  • golangcollege/sessions session module to handle all session operations on the application.
  • bluele/gcache to cache user data to reduce database read load and provide faster
  • bootstrap to provide responsive ui components with nice styling with less css.
  • fontawesome to use custom icons that fontawesome provides. responses.

Testing Modules

  • DATA-DOG/go-sqlmock sql db mock module to mock the database on testing repository layer.
  • golang/mock as a mock module to mock the interfaces and test the objects independently.

About

A social media web application allows user to follow other users, publish new posts, comment on published posts, like posts or comments. Users also has their timelines according to the followed users, trend page that shows the trend posts, search operation to find another users, get mailed on account creation or notifications.

Topics

Resources

Stars

Watchers

Forks