diff --git a/Tsi1.Api/Tsi1.Api/SignalR/ChatHub.cs b/Tsi1.Api/Tsi1.Api/SignalR/ChatHub.cs
new file mode 100644
index 0000000000000000000000000000000000000000..0c54af61a8bd5ac751eec8dd173a9f1140b4f5c8
--- /dev/null
+++ b/Tsi1.Api/Tsi1.Api/SignalR/ChatHub.cs
@@ -0,0 +1,146 @@
+using AutoMapper;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.SignalR;
+using Microsoft.EntityFrameworkCore;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Cryptography.X509Certificates;
+using System.Threading.Tasks;
+using Tsi1.BusinessLayer.Dtos;
+using Tsi1.BusinessLayer.Interfaces;
+using Tsi1.DataLayer;
+using Tsi1.DataLayer.Entities;
+
+namespace Tsi1.Api.SignalR
+{
+    [Authorize]
+    public class ChatHub : Hub
+    {
+        private readonly IMapper _mapper;
+        private readonly IHubContext<PresenceHub> _presenceHub;
+        private readonly PresenceTracker _tracker;
+        private readonly IMessageService _messageService;
+        private readonly IChatService _chatService;
+
+        public ChatHub(IMapper mapper, IHubContext<PresenceHub> presenceHub,
+            PresenceTracker tracker, IMessageService messageService, IChatService chatService)
+        {
+            _tracker = tracker;
+            _presenceHub = presenceHub;
+            _mapper = mapper;
+            _messageService = messageService;
+            _chatService = chatService;
+        }
+
+        public override async Task OnConnectedAsync()
+        {
+            var httpContext = Context.GetHttpContext();
+
+            var otherUserId = int.Parse(httpContext.Request.Query["id"].ToString());
+
+            var userId = int.Parse(Context.User.Claims.FirstOrDefault(x => x.Type == "Id").Value);
+            var tenantId = int.Parse(Context.User.Claims.FirstOrDefault(x => x.Type == "TenantId").Value);
+
+            var groupName = GetGroupName(userId, otherUserId);
+            await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
+
+            await AddToGroup(groupName);
+
+            var messages = await _messageService.GetMessages(userId, otherUserId, tenantId);
+
+            await Clients.Caller.SendAsync("ReceiveMessages", messages);
+        }
+
+        public override async Task OnDisconnectedAsync(Exception exception)
+        {
+
+            await RemoveFromGroup();
+
+            await base.OnDisconnectedAsync(exception);
+        }
+
+        public async Task SendMessage(MessageCreateDto newMessage)
+        {
+            var userId = int.Parse(Context.User.Claims.FirstOrDefault(x => x.Type == "Id").Value);
+            var tenantId = int.Parse(Context.User.Claims.FirstOrDefault(x => x.Type == "TenantId").Value);
+
+            if (userId == newMessage.ReceiverId)
+                throw new HubException("You cannot send messages to yourself");
+
+            var groupName = GetGroupName(userId, newMessage.ReceiverId);
+
+            var group = await _chatService.GetGroupByName(groupName);
+
+            if (group == null)
+            {
+                throw new HubException($"No existe el grupo {groupName}");
+            }
+
+            if (!group.Connections.Any(x => x.UserId == newMessage.ReceiverId))
+            {
+                var connections = await _tracker.GetConnectionsForUser(newMessage.ReceiverId);
+                if (connections != null)
+                {
+                    await _presenceHub.Clients.Clients(connections).SendAsync("NewMessageReceived", userId);
+                }
+            }
+
+            newMessage.SenderId = userId;
+            var messageDto = await _messageService.Send(newMessage);
+
+            await Clients.Group(groupName).SendAsync("NewMessage", messageDto);
+        }
+
+        private async Task<Group> AddToGroup(string groupName)
+        {
+            var group = await _chatService.GetGroupByName(groupName);
+
+            if (group == null)
+            {
+                throw new HubException($"No existe el grupo {groupName}");
+            }
+
+            var userId = int.Parse(Context.User.Claims.FirstOrDefault(x => x.Type == "Id").Value);
+
+            var connection = new Connection
+            {
+                ConnectionId = Context.ConnectionId,
+                UserId = userId,
+            };
+
+            if (group == null)
+            {
+                group = new Group
+                {
+                    Name = groupName,
+                };
+            }
+
+            group.Connections.Add(connection);
+
+            var result = await _chatService.CreateGroup(group);
+
+            if (result.HasError)
+            {
+                throw new HubException($"Error al crear el grupo {group}");
+            }
+
+            return group;
+        }
+
+        private async Task<bool> RemoveFromGroup()
+        {
+            await _chatService.RemoveConnectionFromGroup(Context.ConnectionId);
+
+            return true;
+        }
+
+        private string GetGroupName(int callerId, int otherId)
+        {
+            var compare = callerId < otherId;
+            return compare ? $"{callerId}-{otherId}" : $"{otherId}-{callerId}";
+        }
+    }
+}
diff --git a/Tsi1.Api/Tsi1.Api/SignalR/PresenceHub.cs b/Tsi1.Api/Tsi1.Api/SignalR/PresenceHub.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e606bc97df59fe20017906c434941e3b40ca28a8
--- /dev/null
+++ b/Tsi1.Api/Tsi1.Api/SignalR/PresenceHub.cs
@@ -0,0 +1,41 @@
+using Microsoft.AspNetCore.SignalR;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Tsi1.Api.SignalR
+{
+    public class PresenceHub : Hub
+    {
+        private readonly PresenceTracker _tracker;
+        public PresenceHub(PresenceTracker tracker)
+        {
+            _tracker = tracker;
+        }
+
+        public override async Task OnConnectedAsync()
+        {
+            var userId = int.Parse(Context.User.Claims.FirstOrDefault(x => x.Type == "Id").Value);
+
+            var isOnline = await _tracker.UserConnected(userId, Context.ConnectionId);
+            if (isOnline)
+                await Clients.Others.SendAsync("UserIsOnline", userId);
+
+            var currentUsers = await _tracker.GetOnlineUsers();
+            await Clients.Caller.SendAsync("GetOnlineUsers", currentUsers);
+        }
+
+        public override async Task OnDisconnectedAsync(Exception exception)
+        {
+            var userId = int.Parse(Context.User.Claims.FirstOrDefault(x => x.Type == "Id").Value);
+
+            var isOffline = await _tracker.UserDisconnected(userId, Context.ConnectionId);
+
+            if (isOffline)
+                await Clients.Others.SendAsync("UserIsOffline", userId);
+
+            await base.OnDisconnectedAsync(exception);
+        }
+    }
+}
diff --git a/Tsi1.Api/Tsi1.Api/SignalR/PresenceTracker.cs b/Tsi1.Api/Tsi1.Api/SignalR/PresenceTracker.cs
new file mode 100644
index 0000000000000000000000000000000000000000..f11f18db7d18e01b3a2f8b82d65f8a07dcb3db47
--- /dev/null
+++ b/Tsi1.Api/Tsi1.Api/SignalR/PresenceTracker.cs
@@ -0,0 +1,72 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Tsi1.Api.SignalR
+{
+    public class PresenceTracker
+    {
+        private static readonly Dictionary<int, List<string>> OnlineUsers =
+            new Dictionary<int, List<string>>();
+
+        public Task<bool> UserConnected(int userId, string connectionId)
+        {
+            bool isOnline = false;
+            lock (OnlineUsers)
+            {
+                if (OnlineUsers.ContainsKey(userId))
+                {
+                    OnlineUsers[userId].Add(connectionId);
+                }
+                else
+                {
+                    OnlineUsers.Add(userId, new List<string> { connectionId });
+                    isOnline = true;
+                }
+            }
+
+            return Task.FromResult(isOnline);
+        }
+
+        public Task<bool> UserDisconnected(int userId, string connectionId)
+        {
+            bool isOffline = false;
+            lock (OnlineUsers)
+            {
+                if (!OnlineUsers.ContainsKey(userId)) return Task.FromResult(isOffline);
+
+                OnlineUsers[userId].Remove(connectionId);
+                if (OnlineUsers[userId].Count == 0)
+                {
+                    OnlineUsers.Remove(userId);
+                    isOffline = true;
+                }
+            }
+
+            return Task.FromResult(isOffline);
+        }
+
+        public Task<int[]> GetOnlineUsers()
+        {
+            int[] onlineUsers;
+            lock (OnlineUsers)
+            {
+                onlineUsers = OnlineUsers.OrderBy(k => k.Key).Select(k => k.Key).ToArray();
+            }
+
+            return Task.FromResult(onlineUsers);
+        }
+
+        public Task<List<string>> GetConnectionsForUser(int userId)
+        {
+            List<string> connectionIds;
+            lock (OnlineUsers)
+            {
+                connectionIds = OnlineUsers.GetValueOrDefault(userId);
+            }
+
+            return Task.FromResult(connectionIds);
+        }
+    }
+}
diff --git a/Tsi1.Api/Tsi1.Api/SignalR/VideoHub.cs b/Tsi1.Api/Tsi1.Api/SignalR/VideoHub.cs
new file mode 100644
index 0000000000000000000000000000000000000000..5e62112c24780b458c531ea9e266d70b4966b1e1
--- /dev/null
+++ b/Tsi1.Api/Tsi1.Api/SignalR/VideoHub.cs
@@ -0,0 +1,55 @@
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.SignalR;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Tsi1.BusinessLayer.Helpers;
+
+namespace Tsi1.Api.SignalR
+{
+    [Authorize]
+    public class VideoHub : Hub
+    {
+        public override async Task OnConnectedAsync()
+        {
+            await Groups.AddToGroupAsync(Context.ConnectionId, Context.ConnectionId);
+
+            await Clients.Caller.SendAsync(Context.ConnectionId);
+        }
+
+        [Authorize(Roles = UserTypes.Professor)]
+        public async Task CreateRoom(string roomName)
+        {
+            await Groups.AddToGroupAsync(Context.ConnectionId, roomName);
+
+            await Clients.Caller.SendAsync("CreateRoom", roomName);
+        }
+
+        public async Task Join(string roomName, string offer)
+        {
+
+            Console.WriteLine(offer);
+            await Clients.Group(roomName).SendAsync("Join", Context.ConnectionId, offer);
+
+            await Groups.AddToGroupAsync(Context.ConnectionId, roomName);
+        }
+
+        public async Task Leave(string roomName)
+        {
+            await Groups.RemoveFromGroupAsync(Context.ConnectionId, roomName);
+
+            await Clients.Group(roomName).SendAsync("Leave", Context.ConnectionId);
+        }
+
+        public async Task NotifyUser(string connectionId, string answer)
+        {
+            await Clients.Group(connectionId).SendAsync("NotifyUser", Context.ConnectionId, answer);
+        }
+
+        public async Task ICECandidate(string connectionId, string candidate)
+        {
+            await Clients.Group(connectionId).SendAsync("ICECandidate", Context.ConnectionId, candidate);
+        }
+    }
+}
diff --git a/Tsi1.Api/Tsi1.Api/Startup.cs b/Tsi1.Api/Tsi1.Api/Startup.cs
index 854ba8a94b7a335050dcd3c5d35f1e8c2fa73939..135212e330271889277770863aa98754ea3dcd31 100644
--- a/Tsi1.Api/Tsi1.Api/Startup.cs
+++ b/Tsi1.Api/Tsi1.Api/Startup.cs
@@ -22,6 +22,7 @@ using Microsoft.Extensions.Options;
 using Microsoft.IdentityModel.Tokens;
 using Microsoft.OpenApi.Models;
 using Tsi1.Api.Infrastructure;
+using Tsi1.Api.SignalR;
 using Tsi1.BusinessLayer.Helpers;
 using Tsi1.BusinessLayer.Interfaces;
 using Tsi1.BusinessLayer.Services;
@@ -44,6 +45,8 @@ namespace Tsi1.Api
         {
             services.AddControllers();
 
+            services.AddSignalR();
+
             var isElasticCloud = bool.Parse(Configuration.GetSection("IsElasticCloud").Value);
 
             var postgreSqlSection = isElasticCloud ? "PostgreSqlCloud" : "PostgreSql";
@@ -108,6 +111,23 @@ namespace Tsi1.Api
                     ValidateLifetime = true,
                     ClockSkew = TimeSpan.FromMinutes(1)
                 };
+
+                x.Events = new JwtBearerEvents
+                {
+                    OnMessageReceived = context =>
+                    {
+                        var accessToken = context.Request.Query["access_token"];
+
+                        var path = context.HttpContext.Request.Path;
+                        if (!string.IsNullOrEmpty(accessToken) &&
+                            path.StartsWithSegments("/hubs"))
+                        {
+                            context.Token = accessToken;
+                        }
+
+                        return Task.CompletedTask;
+                    }
+                };
             });
 
             services.AddSwaggerGen(c =>
@@ -180,6 +200,9 @@ namespace Tsi1.Api
             app.UseEndpoints(endpoints =>
             {
                 endpoints.MapControllers();
+                endpoints.MapHub<VideoHub>("hubs/video");
+                endpoints.MapHub<PresenceHub>("hubs/presence");
+                endpoints.MapHub<ChatHub>("hubs/chat");
             });
         }
     }
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/IChatService.cs b/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/IChatService.cs
new file mode 100644
index 0000000000000000000000000000000000000000..cf332b0a11d11b5b77f9058d9d1a95488293b9ea
--- /dev/null
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/IChatService.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using Tsi1.BusinessLayer.Helpers;
+using Tsi1.DataLayer.Entities;
+
+namespace Tsi1.BusinessLayer.Interfaces
+{
+    public interface IChatService
+    {
+        Task<Group> GetGroupByName(string groupName);
+
+        Task<ServiceResult<Group>> CreateGroup(Group group);
+
+        Task<bool> RemoveConnectionFromGroup(string connectionId);
+    }
+}
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Services/ChatService.cs b/Tsi1.Api/Tsi1.BusinessLayer/Services/ChatService.cs
new file mode 100644
index 0000000000000000000000000000000000000000..4e4ac8e76aaca0017a0d35dca73cd1e57503da3e
--- /dev/null
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Services/ChatService.cs
@@ -0,0 +1,60 @@
+using Microsoft.EntityFrameworkCore;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using Tsi1.BusinessLayer.Helpers;
+using Tsi1.BusinessLayer.Interfaces;
+using Tsi1.DataLayer;
+using Tsi1.DataLayer.Entities;
+
+namespace Tsi1.BusinessLayer.Services
+{
+    public class ChatService : IChatService
+    {
+        private readonly Tsi1Context _tsi1Context;
+
+        public ChatService(Tsi1Context tsi1Context)
+        {
+            _tsi1Context = tsi1Context;
+        }
+        public async Task<ServiceResult<Group>> CreateGroup(Group group)
+        {
+            var result = new ServiceResult<Group>();
+
+            _tsi1Context.Groups.Add(group);
+            try
+            {
+                await _tsi1Context.SaveChangesAsync();
+            }
+            catch (Exception)
+            {
+                result.HasError = true;
+            }
+
+            return result;
+        }
+
+        public async Task<Group> GetGroupByName(string groupName)
+        {
+            var result = new ServiceResult<Group>();
+
+            var group = await _tsi1Context.Groups.FirstOrDefaultAsync(x => x.Name == groupName);
+
+            return group;
+        }
+
+        public async Task<bool> RemoveConnectionFromGroup(string connectionId)
+        {
+            var connection = await _tsi1Context.Connections.FirstOrDefaultAsync(x => x.ConnectionId == connectionId);
+
+            if (connection != null)
+            {
+                connection.Group = null;
+                await _tsi1Context.SaveChangesAsync();
+            }
+
+            return true;
+        }
+    }
+}
diff --git a/Tsi1.Api/Tsi1.DataLayer/Entities/Connection.cs b/Tsi1.Api/Tsi1.DataLayer/Entities/Connection.cs
new file mode 100644
index 0000000000000000000000000000000000000000..2e2e9366bdd5dd35d36650f805c1672806f08f69
--- /dev/null
+++ b/Tsi1.Api/Tsi1.DataLayer/Entities/Connection.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tsi1.DataLayer.Entities
+{
+    public class Connection
+    {
+        public string ConnectionId { get; set; }
+        public int UserId { get; set; }
+        public Group Group { get; set; }
+    }
+}
diff --git a/Tsi1.Api/Tsi1.DataLayer/Entities/Group.cs b/Tsi1.Api/Tsi1.DataLayer/Entities/Group.cs
new file mode 100644
index 0000000000000000000000000000000000000000..38291ebfd52def7d9d2e21d177a72fc191b7129c
--- /dev/null
+++ b/Tsi1.Api/Tsi1.DataLayer/Entities/Group.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tsi1.DataLayer.Entities
+{
+    public class Group
+    {
+        public Group()
+        {
+            Connections = new List<Connection>();
+        }
+
+        public string Name { get; set; }
+        public ICollection<Connection> Connections { get; set; }
+    }
+}
diff --git a/Tsi1.Api/Tsi1.DataLayer/EntityConfiguration/ConnectionConfiguration.cs b/Tsi1.Api/Tsi1.DataLayer/EntityConfiguration/ConnectionConfiguration.cs
new file mode 100644
index 0000000000000000000000000000000000000000..7c085f952b6ad8859207d0eb4371c69d0edb2301
--- /dev/null
+++ b/Tsi1.Api/Tsi1.DataLayer/EntityConfiguration/ConnectionConfiguration.cs
@@ -0,0 +1,24 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Tsi1.DataLayer.Entities;
+
+namespace Tsi1.DataLayer.EntityConfiguration
+{
+    public class ConnectionConfiguration : IEntityTypeConfiguration<Connection>
+    {
+        public void Configure(EntityTypeBuilder<Connection> builder)
+        {
+            builder.HasKey(x => x.ConnectionId);
+
+            builder.Property(x => x.UserId)
+                .IsRequired();
+
+            builder.HasOne(x => x.Group)
+                .WithMany(x => x.Connections);
+
+        }
+    }
+}
diff --git a/Tsi1.Api/Tsi1.DataLayer/EntityConfiguration/GroupConfiguration.cs b/Tsi1.Api/Tsi1.DataLayer/EntityConfiguration/GroupConfiguration.cs
new file mode 100644
index 0000000000000000000000000000000000000000..9e64b95e6eb1fca0fff085e53679025dfa95728a
--- /dev/null
+++ b/Tsi1.Api/Tsi1.DataLayer/EntityConfiguration/GroupConfiguration.cs
@@ -0,0 +1,17 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Tsi1.DataLayer.Entities;
+
+namespace Tsi1.DataLayer.EntityConfiguration
+{
+    public class GroupConfiguration : IEntityTypeConfiguration<Group>
+    {
+        public void Configure(EntityTypeBuilder<Group> builder)
+        {
+            builder.HasKey(x => x.Name);
+        }
+    }
+}
diff --git a/Tsi1.Api/Tsi1.DataLayer/Migrations/20201029004502_added-entities-Group-and-Connection.Designer.cs b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201029004502_added-entities-Group-and-Connection.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..5dba9e6375b1de286038bffe7e78d016f6cdd791
--- /dev/null
+++ b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201029004502_added-entities-Group-and-Connection.Designer.cs
@@ -0,0 +1,491 @@
+// <auto-generated />
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+using Tsi1.DataLayer;
+
+namespace Tsi1.DataLayer.Migrations
+{
+    [DbContext(typeof(Tsi1Context))]
+    [Migration("20201029004502_added-entities-Group-and-Connection")]
+    partial class addedentitiesGroupandConnection
+    {
+        protected override void BuildTargetModel(ModelBuilder modelBuilder)
+        {
+#pragma warning disable 612, 618
+            modelBuilder
+                .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
+                .HasAnnotation("ProductVersion", "3.1.4")
+                .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Connection", b =>
+                {
+                    b.Property<string>("ConnectionId")
+                        .HasColumnType("text");
+
+                    b.Property<string>("GroupName")
+                        .HasColumnType("text");
+
+                    b.Property<int>("UserId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("ConnectionId");
+
+                    b.HasIndex("GroupName");
+
+                    b.ToTable("Connections");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Course", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("character varying(50)");
+
+                    b.Property<int>("TenantId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("TenantId");
+
+                    b.HasIndex("Name", "TenantId")
+                        .IsUnique();
+
+                    b.ToTable("Courses");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Forum", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<int>("CourseId")
+                        .HasColumnType("integer");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("character varying(50)");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("CourseId", "Name")
+                        .IsUnique();
+
+                    b.ToTable("Forums");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.ForumUser", b =>
+                {
+                    b.Property<int>("ForumId")
+                        .HasColumnType("integer");
+
+                    b.Property<int>("UserId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("ForumId", "UserId");
+
+                    b.HasIndex("UserId");
+
+                    b.ToTable("ForumUsers");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Group", b =>
+                {
+                    b.Property<string>("Name")
+                        .HasColumnType("text");
+
+                    b.HasKey("Name");
+
+                    b.ToTable("Groups");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Post", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<DateTime>("Date")
+                        .HasColumnType("timestamp without time zone");
+
+                    b.Property<int>("ForumId")
+                        .HasColumnType("integer");
+
+                    b.Property<string>("Title")
+                        .IsRequired()
+                        .HasColumnType("character varying(100)");
+
+                    b.Property<int>("UserId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("UserId");
+
+                    b.HasIndex("ForumId", "Title")
+                        .IsUnique();
+
+                    b.ToTable("Posts");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.PostMessage", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<string>("Content")
+                        .IsRequired()
+                        .HasColumnType("character varying(10485760)");
+
+                    b.Property<DateTime>("Date")
+                        .HasColumnType("timestamp without time zone");
+
+                    b.Property<int>("PostId")
+                        .HasColumnType("integer");
+
+                    b.Property<int>("UserId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("PostId");
+
+                    b.HasIndex("UserId");
+
+                    b.ToTable("PostMessages");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Professor", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<string>("IdentityCard")
+                        .IsRequired()
+                        .HasColumnType("character varying(50)");
+
+                    b.Property<int>("TenantId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("TenantId");
+
+                    b.HasIndex("IdentityCard", "TenantId")
+                        .IsUnique();
+
+                    b.ToTable("Professors");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.ProfessorCourse", b =>
+                {
+                    b.Property<int>("ProfessorId")
+                        .HasColumnType("integer");
+
+                    b.Property<int>("CourseId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("ProfessorId", "CourseId");
+
+                    b.HasIndex("CourseId");
+
+                    b.ToTable("ProfessorCourses");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Student", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<int>("Age")
+                        .HasColumnType("integer");
+
+                    b.Property<string>("IdentityCard")
+                        .IsRequired()
+                        .HasColumnType("character varying(50)");
+
+                    b.Property<int>("TenantId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("TenantId");
+
+                    b.HasIndex("IdentityCard", "TenantId")
+                        .IsUnique();
+
+                    b.ToTable("Students");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.StudentCourse", b =>
+                {
+                    b.Property<int>("StudentId")
+                        .HasColumnType("integer");
+
+                    b.Property<int>("CourseId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("StudentId", "CourseId");
+
+                    b.HasIndex("CourseId");
+
+                    b.ToTable("StudentCourses");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Tenant", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("character varying(50)");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("Name")
+                        .IsUnique();
+
+                    b.ToTable("Tenants");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.User", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<string>("Email")
+                        .IsRequired()
+                        .HasColumnType("character varying(255)");
+
+                    b.Property<string>("FirstName")
+                        .IsRequired()
+                        .HasColumnType("character varying(255)");
+
+                    b.Property<string>("LastName")
+                        .IsRequired()
+                        .HasColumnType("character varying(255)");
+
+                    b.Property<string>("Password")
+                        .IsRequired()
+                        .HasColumnType("character varying(255)");
+
+                    b.Property<int?>("ProfessorId")
+                        .HasColumnType("integer");
+
+                    b.Property<int?>("StudentId")
+                        .HasColumnType("integer");
+
+                    b.Property<int>("TenantId")
+                        .HasColumnType("integer");
+
+                    b.Property<int>("UserTypeId")
+                        .HasColumnType("integer");
+
+                    b.Property<string>("Username")
+                        .IsRequired()
+                        .HasColumnType("character varying(50)");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("ProfessorId")
+                        .IsUnique();
+
+                    b.HasIndex("StudentId")
+                        .IsUnique();
+
+                    b.HasIndex("TenantId");
+
+                    b.HasIndex("UserTypeId");
+
+                    b.HasIndex("Username", "TenantId")
+                        .IsUnique();
+
+                    b.ToTable("Users");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.UserType", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("integer")
+                        .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("character varying(50)");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("Name")
+                        .IsUnique();
+
+                    b.ToTable("UserTypes");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Connection", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Group", "Group")
+                        .WithMany("Connections")
+                        .HasForeignKey("GroupName");
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Course", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant")
+                        .WithMany("Courses")
+                        .HasForeignKey("TenantId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Forum", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Course", "Course")
+                        .WithMany("Forums")
+                        .HasForeignKey("CourseId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.ForumUser", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Forum", "Forum")
+                        .WithMany("ForumUsers")
+                        .HasForeignKey("ForumId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("Tsi1.DataLayer.Entities.User", "User")
+                        .WithMany("ForumUsers")
+                        .HasForeignKey("UserId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Post", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Forum", "Forum")
+                        .WithMany("Posts")
+                        .HasForeignKey("ForumId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("Tsi1.DataLayer.Entities.User", "User")
+                        .WithMany("Posts")
+                        .HasForeignKey("UserId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.PostMessage", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Post", "Post")
+                        .WithMany("PostMessages")
+                        .HasForeignKey("PostId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("Tsi1.DataLayer.Entities.User", "User")
+                        .WithMany("PostMessages")
+                        .HasForeignKey("UserId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Professor", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant")
+                        .WithMany("Professors")
+                        .HasForeignKey("TenantId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.ProfessorCourse", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Course", "Course")
+                        .WithMany("ProfessorCourses")
+                        .HasForeignKey("CourseId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("Tsi1.DataLayer.Entities.Professor", "Professor")
+                        .WithMany("ProfessorCourses")
+                        .HasForeignKey("ProfessorId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Student", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant")
+                        .WithMany("Students")
+                        .HasForeignKey("TenantId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.StudentCourse", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Course", "Course")
+                        .WithMany("StudentCourses")
+                        .HasForeignKey("CourseId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("Tsi1.DataLayer.Entities.Student", "Student")
+                        .WithMany("StudentCourses")
+                        .HasForeignKey("StudentId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.User", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Professor", "Professor")
+                        .WithOne("User")
+                        .HasForeignKey("Tsi1.DataLayer.Entities.User", "ProfessorId");
+
+                    b.HasOne("Tsi1.DataLayer.Entities.Student", "Student")
+                        .WithOne("User")
+                        .HasForeignKey("Tsi1.DataLayer.Entities.User", "StudentId");
+
+                    b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant")
+                        .WithMany("Users")
+                        .HasForeignKey("TenantId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("Tsi1.DataLayer.Entities.UserType", "UserType")
+                        .WithMany()
+                        .HasForeignKey("UserTypeId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+#pragma warning restore 612, 618
+        }
+    }
+}
diff --git a/Tsi1.Api/Tsi1.DataLayer/Migrations/20201029004502_added-entities-Group-and-Connection.cs b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201029004502_added-entities-Group-and-Connection.cs
new file mode 100644
index 0000000000000000000000000000000000000000..dfece7bb6d2fa79e3edd1afe268602d00477a253
--- /dev/null
+++ b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201029004502_added-entities-Group-and-Connection.cs
@@ -0,0 +1,54 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+namespace Tsi1.DataLayer.Migrations
+{
+    public partial class addedentitiesGroupandConnection : Migration
+    {
+        protected override void Up(MigrationBuilder migrationBuilder)
+        {
+            migrationBuilder.CreateTable(
+                name: "Groups",
+                columns: table => new
+                {
+                    Name = table.Column<string>(nullable: false)
+                },
+                constraints: table =>
+                {
+                    table.PrimaryKey("PK_Groups", x => x.Name);
+                });
+
+            migrationBuilder.CreateTable(
+                name: "Connections",
+                columns: table => new
+                {
+                    ConnectionId = table.Column<string>(nullable: false),
+                    UserId = table.Column<int>(nullable: false),
+                    GroupName = table.Column<string>(nullable: true)
+                },
+                constraints: table =>
+                {
+                    table.PrimaryKey("PK_Connections", x => x.ConnectionId);
+                    table.ForeignKey(
+                        name: "FK_Connections_Groups_GroupName",
+                        column: x => x.GroupName,
+                        principalTable: "Groups",
+                        principalColumn: "Name",
+                        onDelete: ReferentialAction.Restrict);
+                });
+
+            migrationBuilder.CreateIndex(
+                name: "IX_Connections_GroupName",
+                table: "Connections",
+                column: "GroupName");
+        }
+
+        protected override void Down(MigrationBuilder migrationBuilder)
+        {
+            migrationBuilder.DropTable(
+                name: "Connections");
+
+            migrationBuilder.DropTable(
+                name: "Groups");
+        }
+    }
+}
diff --git a/Tsi1.Api/Tsi1.DataLayer/Migrations/Tsi1ContextModelSnapshot.cs b/Tsi1.Api/Tsi1.DataLayer/Migrations/Tsi1ContextModelSnapshot.cs
index 4c6d381fbdcd5201077e62e722bd7916da810a66..9f2b80da778b9cfc5bb392f6faa481cab6f4eea7 100644
--- a/Tsi1.Api/Tsi1.DataLayer/Migrations/Tsi1ContextModelSnapshot.cs
+++ b/Tsi1.Api/Tsi1.DataLayer/Migrations/Tsi1ContextModelSnapshot.cs
@@ -19,6 +19,24 @@ namespace Tsi1.DataLayer.Migrations
                 .HasAnnotation("ProductVersion", "3.1.4")
                 .HasAnnotation("Relational:MaxIdentifierLength", 63);
 
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Connection", b =>
+                {
+                    b.Property<string>("ConnectionId")
+                        .HasColumnType("text");
+
+                    b.Property<string>("GroupName")
+                        .HasColumnType("text");
+
+                    b.Property<int>("UserId")
+                        .HasColumnType("integer");
+
+                    b.HasKey("ConnectionId");
+
+                    b.HasIndex("GroupName");
+
+                    b.ToTable("Connections");
+                });
+
             modelBuilder.Entity("Tsi1.DataLayer.Entities.Course", b =>
                 {
                     b.Property<int>("Id")
@@ -103,6 +121,16 @@ namespace Tsi1.DataLayer.Migrations
                     b.ToTable("ForumUsers");
                 });
 
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Group", b =>
+                {
+                    b.Property<string>("Name")
+                        .HasColumnType("text");
+
+                    b.HasKey("Name");
+
+                    b.ToTable("Groups");
+                });
+
             modelBuilder.Entity("Tsi1.DataLayer.Entities.Post", b =>
                 {
                     b.Property<int>("Id")
@@ -338,6 +366,13 @@ namespace Tsi1.DataLayer.Migrations
                     b.ToTable("UserTypes");
                 });
 
+            modelBuilder.Entity("Tsi1.DataLayer.Entities.Connection", b =>
+                {
+                    b.HasOne("Tsi1.DataLayer.Entities.Group", "Group")
+                        .WithMany("Connections")
+                        .HasForeignKey("GroupName");
+                });
+
             modelBuilder.Entity("Tsi1.DataLayer.Entities.Course", b =>
                 {
                     b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant")
diff --git a/Tsi1.Api/Tsi1.DataLayer/Tsi1Context.cs b/Tsi1.Api/Tsi1.DataLayer/Tsi1Context.cs
index 91401cfd792dbfc0ab8f62ebd7dd6e6f7277dda4..d442517b50b82f0ebd6f86812ac69f463cd16239 100644
--- a/Tsi1.Api/Tsi1.DataLayer/Tsi1Context.cs
+++ b/Tsi1.Api/Tsi1.DataLayer/Tsi1Context.cs
@@ -21,6 +21,8 @@ namespace Tsi1.DataLayer
         public DbSet<PostMessage> PostMessages { get; set; }
         public DbSet<Tenant> Tenants { get; set; }
         public DbSet<ForumUser> ForumUsers { get; set; }
+        public DbSet<Group> Groups { get; set; }
+        public DbSet<Connection> Connections { get; set; }
         public DbSet<File> Files { get; set; }
 
 
@@ -40,6 +42,8 @@ namespace Tsi1.DataLayer
             modelBuilder.ApplyConfiguration(new PostMessageConfiguration());
             modelBuilder.ApplyConfiguration(new TenantConfiguration());
             modelBuilder.ApplyConfiguration(new ForumUserConfiguration());
+            modelBuilder.ApplyConfiguration(new GroupConfiguration());
+            modelBuilder.ApplyConfiguration(new ConnectionConfiguration());
             modelBuilder.ApplyConfiguration(new FileConfiguration());
         }
     }